事务
概念
如果一个包含多个步骤的业务操作,被事务管理,要么同时成功,要么同时失败。
操作
- 开启事务:start transaction
- 回滚:rollback
- 提交:commit
在MySQL数据库中事务默认自动提交
- 一条DML(增删改)语句会自动提交一次事务。
- 事务提交的两种方式:
- 自动提交
- 手动提交:需要先开启事务,再提交(Oracle数据库默认手动提交事务)
- 修改事务的默认提交方式
- 查看事务的默认提交方式:SELECT @@autocommit
- 修改默认提交方式:set @@autocommit=0
事务的四大特性
- 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
- 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
- 隔离性:多个事务之间。相互独立。
- 一致性:事务操作前后数据总量不变。
事务的隔离级别
概念:多个事务之间是相互独立的。但是多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
存在问题:
- 脏读:一个事务,读取到另一个事务中没有提交的数据。
- 不可重复读:在同一个事务中,两次读取到的数据不一样。
- 幻读:一个事务操作(DML)数据表中所有的记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
隔离级别:
- read uncommitted:读未提交
- read committed:读已提交(Oracle)
- repeatable read:可重复读(MySQL默认)
- serializable:串行化
注意:隔离级别从小到大安全性越来越高,效率越来越低
数据库查询隔离级别: select @@tx_isolation;
数据库设置隔离级别:set global transaction isolation level 级别字符串;
JDBC
概念
Java DataBase Connectivity Java数据库连接,Java语言操作数据库
JDBC本质:Sun公司定义了一套操作所有关系型数据库的规则(接口)。各个数据库厂商实现这套接口,提供数据库驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
步骤
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象Connection
- 定义sql
- 获取执行sql语句的对象Statement
- 执行sql。接受返回结果
- 处理结果
- 释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy", "root", "12345678"); String sql = "update account set money = 20 where id = 1"; Statement statement = conn.createStatement(); int count = statement.executeUpdate(sql); System.out.println(count); statement.close(); conn.close(); }
|
详解各个对象
DriverManager:驱动管理对象
功能:
- 注册驱动:告诉程序该使用哪一个数据库jar包
- static void registerDriver(Driver driver):注册与给定的驱动程序。
- 我们使用Class.forName(“com.mysql.jdbc.Driver”)
- 通过静态代码块,使类被加载时,方法就被执行
- 5版本之后可以省略注册驱动的步骤
- 获取数据库连接:
- 参数:
- URL: 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
- user:用户名
- password:密码
- 如果连接的是本机的mysql服务器,并且mysql服务器默认端口是3306,则URL可以简写为:jdbc:mysql:///数据库名称
Connection:数据连接对象
功能:
- 获取执行sql对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
- 管理事务:
- 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务。
- 提交事务:void commit()
- 回滚事务:void rollback()
Statement:执行sql的对象
- 执行sql
- boolean execute(String sql):可以执行任意的sql
- int executeUpdate(String sql):执行DML(insert、update、delete)语句,DDL语句(create,alter、drop)语句。
- ResultSet executeQuery(String sql):执行DQL(select)语句。
ResultSet:结果集对象
用来封装查询的结果
- next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
- getXxx(参数):获取数据,XXX代表数据类型,如 int getInt(),参数:
- int:代表列的编号,从1开始。
- String:代表列的名称
- 使用步骤:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
PreparedStatement:执行sql的对象
SQL注入问题:在拼接sql时,有一些SQL关键字参与字符串的拼接,会造成安全性问题。
例如用户随便输入密码,再加一句 OR ‘a’ = ‘a’,这就恒成立了
解决SQL注入问题:使用PreparedStatement对象来解决。
预编译的SQL:参数使用?作为占位符
- setString(1,”root”);
- setInt(2,12345678);
后期都会使用PrepareStatement对象,因为安全性和效率都更高。
抽取JBDBC工具类
目的:简化书写
分析:
- 抽取一个方法获取连接对象,通过配置文件
- 抽取一个方法释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; static { Properties pro = new Properties(); try { ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL resource = classLoader.getResource("jdbc.properties"); String path = resource.getPath();
pro.load(new FileReader(path)); url = pro.getProperty("url"); password = pro.getProperty("password"); user = pro.getProperty("user"); driver = pro.getProperty("driver"); Class.forName(driver); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }
public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url,user,password); }
public static void close(Statement s,Connection conn){ if(s!=null){ try{ s.close(); }catch(SQLException e){ e.printStackTrace(); } }
if(conn!=null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } }
public static void close(ResultSet rs,Statement s, Connection conn){
if(rs!=null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } }
if(s!=null){ try{ s.close(); }catch(SQLException e){ e.printStackTrace(); } }
if(conn!=null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } }
}
|
数据库连接池
概念
其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户访问完之后,会讲连接对象归还给容器。
好处:
实现
标准接口:DataSource javax.sql包下的
方法:
- 获取连接:getConnection()
- 归还连接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()是归还连接,而不是关闭连接。
一般我们不去实现它,有数据库厂商来实现
- c3p0:较老
- Druid:数据库连接池实现技术,由阿里巴巴提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| public class JDBCUtils { private static DataSource ds;
static{ Properties pro = new Properties(); try { pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } }
public static Connection getConnection() throws SQLException { return ds.getConnection(); }
public static void close(Statement stmt,Connection conn){ close(null,stmt,conn); }
public static void close(Resultset rs, Statement stmt, Connection conn){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } }
if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } }
if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
public static DataSource getDataSource(){ return ds; } }
|
Spring JDBC
spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象来简化JDBC的框架。
步骤:
- 导入jar包
- 创建JDBCTemplate对象。依赖于数据源DataSource
- JdbcTemplate template = new jdbcTemplate(ds);
- 调用JdbcTemplate的方法来完成CRUD操作
- update():执行增删改语句
- queryForMap():查询结果,将结果集封装为Map集合
- 查询的结果集长度只能是1,将列名作为key,将值作为value,封装为Map。
- queryFoeList():查询结构将结果封装为List集合
- query():查询结构,将结果封装为JavaBean对象
- queryForObject():查询结果,将结果封装为对象
1 2 3 4 5 6 7 8 9 10 11 12
| List<Emp> list = template.query(sql,new RowMapper<Emp>(){
@Override public Emp mapRow(Result rs,int i)throws SQLException{ Emp emp = new Emp(); int id = rs.getInt("id"); String name = rs.getString("name"); emp.setId(id); emp.setName(name); return emp; } });
|
上面是自己写的实现类,也可以使用提供好的实现类1
| List<Emp> list = template.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class));
|
上面的一个问题在于,如果我们定义的类的属性是基本数据类型,就无法接收null,可以将属性的类型设置为引用类型,比如Integer。