数据库(三)
一、事务
1、例子引入
例子:
现在有这么个情况:
有两个人需要转账,一方转给另一方,按照之前的操作是,甲的账户扣500,乙的账户再加500。问题是如果甲发起了转账操作,钱扣了,这个时候数据库宕机了,再启动数据库,就会发现甲的钱扣了,但是这钱并没有到乙这边来。
所以一个解决方案就是,这两个sql语句为一组,同时执行,要么大家都成功,要么大家都失败。这种执行方式j就是通过事务来管理。
2、事务的概念:
3、事务执行的指令
4、事务实现
素材:
通过事务实现转账功能:
SELECT @@autocommit
SET autocommit=0;
START TRANSACTION;
UPDATE account SET amoney=500 WHERE aname ="华子";
UPDATE account SET amoney=2500 WHERE aname ="刘健";
ROLLBACK;
COMMIT;
注意:数据库默认自动提交,我们需要自己设置手动提交;想要使用事务,需要先开启事务;接着就是写 sql 语句;然后有个回滚功能,在提交事务之前,无论执行了多少操作,都可以通过回滚功能返回到未执行操作之前的状态;当操作确定无误后,使用提交事务功能,确认最终状态。切记,提交事务之后就不能再回滚。
5、事务执行的原理
6、事务的特点(ACID)
二、视图
1、视图的概念
2、视图的作用
说人话就是: 1、查询出来的表,是一个虚拟表,即便这个虚拟表被更改了,也不会动到数据库里面的那份原表,是对数据库里面的表的一种保护。
2、有些数据经常会被查看,比如首页的数据,或者搜索出来的第一页的数据,如果经常加载这些数据,会加大数据库的负担,可以通过视图加载这些数据形成一个虚拟表,这样每次查看只会看到这张虚拟表,而不用每次都搜索数据库的数据,减少了数据库的负担。
3、创建视图的语法
1.语法:
create view 视图的名称 as 查询语句
2.例子:
CREATE VIEW v_s AS SELECT ename,eage FROM emp
4、查询视图
1.语法:
select * from 视图名称
2.例子:
SELECT * FROM v_s
5、修改视图
1.语法:
alter view 视图名称 as 查询语句
2.例子:
ALTER VIEW v_s AS SELECT ename FROM emp
6、删除视图
1.语法:
drop view 视图的名称
2.例子
DROP VIEW v_s
三、DCL(对用户以及权限的操作)
1、查询用户
查询数据库的用户,需要先切入到某数据库中,然后再输入查询语句:
2、创建用户
1.语法:
create user '用户名'@'主机名' IDENTIFIED by '密码'
2.例子:创建一个huazi用户
CREATE USER 'huazi'@'localhost' IDENTIFIED BY '123'
3、删除用户
1.语法:
drop user '用户名'@'主机名'
2.例子:
DROP USER 'huazi'@'localhost'
4、修改用户密码
1.语法:
UPDATE USER SET PASSWORD =PASSWORD('新密码') WHERE USER="用户名"
2.例子:
UPDATE USER SET PASSWORD =PASSWORD('abc') WHERE USER="liujian"
3.说明:
msyql中的密码使用的是 md5 加密。
5、查询权限
1.语法:
show grants for '用户名'@'主机名'
2.例子:
SHOW GRANTS FOR 'root'@'%'
6、设置权限
1.语法:
GRANT 权限的类型 on 库名.表名 to ‘用户名’@ '主机名'
2.例子:
GRANT SELECT DELETE UPDATE INSERT ON *.* TO 'hua'@'localhost'
7、撤销权限
1.语法:
revoke 权限的类型 on 库名.表名 from ‘用户名’@ '主机名'
2.说明:
需要重启服务之后才能生效
四、JDBC
1、JDBC的引入
之前都是通过客户端连接数据库:
在实际的开发中,更多的是通过代码来操作数据库
2、jdbc概念以及导入和基础搭建
a、jdbc是什么?
b、jdbc连接的原理
c、驱动包
如果要使用 jdbc ,首先要准备跟数据库版本相匹配的驱动包。
d、基础搭建
创建 lib 文件夹:
粘贴驱动包进来:
然后右键,添加项目依赖:
就完成了基础的搭建
3、jdbc操作数据库
a、操作步骤
b、开始代码输入
接着就是通过字符串保存 sql 语句,然后执行这个 sql 语句(这里的是增 / 删 / 改):
会返回一个 int 值,如果修改成功则是返回 1,否则返回 0。
最后自然是关闭资源:
那么完整代码:
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
Statement sta = con.createStatement();
String sql = "insert into account values(6,'测试',2222) ";
int num = sta.executeUpdate(sql);
System.out.println(num);
sta.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
4、jdbc 的 API 介绍
5、方法的介绍
6、ResultSet 结果集
注意:上面方法中 ,getString 并不是唯一,还有其他很多方法,比如,getDouble 方法等等,这个方法主要是取数据库中的列名(字段名)。
next()方法的使用类似于迭代器的next()
使用方式:
上图中,一般都不这么取,一般是根据列名来取:
7、小案例——登录功能的实现
public static void main(String[] args) throws SQLException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名");
String username = sc.next();
System.out.println("请输入密码");
int pwd = sc.nextInt();
System.out.println(checkLogin(username, pwd));
}
public static boolean checkLogin(String username,int pwd) throws SQLException {
Connection con = null;
Statement sta = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
sta = con.createStatement();
String sql = "select * from admin where username='"+username+"'"+"and pwd='"+pwd+"'";
System.out.println(sql);
rs = sta.executeQuery(sql);
if (rs.next()){
return true;
}
return false;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
finally {
if (rs!=null){
rs.close();
}
if (sta!=null){
sta.close();
}
if (con!=null){
con.close();
}
}
return false;
}
8、PreparedStatement
a、登录功能中的 sql 代码缺陷——sql语句注入问题
在上面得登录功能代码中,sql 语句有问题,可以看到这个执行语句最终会判断是否返回 true。如果外部某人通过某手段,向这个sql语句中再加入一些语句,比如 or 1=1,因为 or 语句有一个为真则为真,且 1=1,这个语句恒为真,这时候就会出现重大问题:
b、防止 sql 注入
c、PreparedStatement 特点和方法
PreparedStatement 不会直接扔进数据库,而是先检查语句是否正确,没问题才放到数据库,这种方式效率更高。
d、PreparedStatement 的使用
原先代码中接收用户输入的方式是这样: 通过 PreparedStatement 对象的使用,则不用这种方法;而是需要填数据的地方先通过占位符代替这个位置,然后判断是否合理合法,如果合理合法,则继续执行,再通过 setXXX方法去填充这个值,然后自后再提交语句。
e、通过 PreparedStatement 优化登录功能
所以通过这种方式修改的登录代码为:
public static void main(String[] args) throws SQLException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名");
String username = sc.next();
System.out.println("请输入密码");
int pwd = sc.nextInt();
System.out.println(checkLogin(username, pwd));
}
public static boolean checkLogin(String username,int pwd) throws SQLException {
Connection con = null;
Statement sta = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
String sql = "select * from admin where username=? and pwd=? ";
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1,username);
ps.setInt(2,pwd);
System.out.println(sql);
rs = ps.executeQuery();
if (rs.next()){
return true;
}
return false;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
finally {
if (rs!=null){
rs.close();
}
if (sta!=null){
sta.close();
}
if (con!=null){
con.close();
}
}
return false;
}
9、工具类的封装(练习)
可以发现,上面的代码太多重复了,每次需要用的时候都一大串代码。可以把他们封装起来,只需要输入 sql 语句,再输入相应的数据,即可完成想要的功能。
工具类:
public class BaseDao {
public static final String url = "jdbc:mysql:///test";
public static final String driver = "com.mysql.jdbc.Driver";
public static final String username = "root";
public static final String password = "root";
public static Connection getCon(){
Connection con = null;
try {
Class.forName(driver);
con = DriverManager.getConnection(url,username,password);
return con;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static int update(String sql,Object obj[]){
int num = 0;
Connection con = null;
PreparedStatement ps = null;
try {
con = getCon();
ps = con.prepareStatement(sql);
if (obj != null && obj.length>0){
for (int i = 0; i < obj.length; i++) {
ps.setObject(i+1,obj[i]);
}
}
num = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(con,ps,null);
}
return num;
}
public static ResultSet getResultSet(String sql,Object obj[]){
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = getCon();
ps = con.prepareStatement(sql);
if (obj != null && obj.length>0){
for (int i = 0; i < obj.length; i++) {
ps.setObject(i+1,obj[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
public static void closeAll(Connection con,PreparedStatement ps,ResultSet rs){
try {
if (con != null){
con.close();
}
if (ps != null){
con.close();
}
if (con != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
测试类:
public static void main(String[] args) throws SQLException {
String sql = "select * from admin where username=?";
Object obj[] ={"admin"};
ResultSet rs = BaseDao.getResultSet(sql,obj);
while (rs.next()){
System.out.println(rs.getString("username"));
}
}
|