一、JDBC优化以及工具类的封装
1.1现有问题:
我们在使用JDBC的过程中,发现部分代码存在冗余的问题:创建连接池;获取连接;连接的回收。
1.2.JDBC工具类封装V1.0
(1)维护一个连接池对象;
(2)对外提供在连接池中获取连接的方法;
(3)对外提供回收连接的方法;
注意:工具类仅对外提供共性的功能代码,所以方法均为静态方法;
resources/db.properties配置文件:
# druid连接池需要的配置参数,key固定命名。
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=atguigu
url=jdbc:mysql:///atguigu
public class JDBCUtil {
//创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的
private static DataSource dataSource;
//在项目启动时,即创建连接池对象,赋值给dataSource
static {
try {
Properties properties = new Properties();
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
}catch(Exception e){
throw new RuntimeException(e);
}
}
public static Connection getconnection(){
try {
return dataSource.getConnection();
} catch(SQLException e){
throw new RuntimeException(e);
}
}
public static void release(Connection connection){
try {
connection.close();
} catch (SQLException e){
throw new RuntimeException(e);
}
}
}
1.3.ThreadLocal
JDK1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、Session等。
ThreadLocal用于保存某个线程共享变量,原因是在Java中每一个线程对象中都有一个ThreadLocalMap<ThreadLocal,Object>,其key就是一个ThreadLocal而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
ThreadLocal对象.get:获取ThreadLocal中当前线程共享变量的值;
ThreadLocal对象.set:设置ThreadLocal中当前线程共享变量的值;
ThreadLocal对象.remove:移除ThreadLocal中当前线程共享变量的值;
1.4.JDBC工具类封装V2.0
(1)维护一个连接池对象,同时维护了一个线程绑定变量的ThreadLocal对象;
(2)对外提供在连接池中获取连接的方法;
(3)对外提供回收连接的方法;
public class JDBCUtil {
//创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
//在项目启动时,即创建连接池对象,赋值给dataSource
static {
try {
Properties properties = new Properties();
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
}catch(Exception e){
throw new RuntimeException(e);
}
}
public static Connection getConnection(){
try {
//在ThreadLocal中获取Connection
Connection connection = threadLocal.get();
//threadLocal里没有存储Connection,也就是第一次获取
if(connection == null){
//在连接池中获取一个连接,存储在threadLocal里
connection = dataSource.getConnection();
threadLocal.set(connection);
}
return connection;
} catch(SQLException e){
throw new RuntimeException(e);
}
}
public static void release(connection connection){
try {
Connection connection = threadLocal.get();
if(connection!=null){
//从threadLocal中移除当前已经存储的Connection对象
threadLocal.remove();
//将Connection对象归还给连接池
connection.close();
}
}catch(SQLException e){
throw new RuntimeException(e);
}
}
}
二、DAO封装以及BaseDAO工具类
2.1.DAO概念:
DAO:Data Access Object,数据访问对象。Java是面向对象语言,数据在Java中通常以对象的形式存在。一张表对应一个实体类,一张表的操作对应一个DAO对象。在Java操作数据库时,我们会将对同一张表的增删改査操作统一维护起来,维护的这个类就是DAO层。DAO层只关注对数据库的操作,供业务层Service调用,将职责划分清楚。
2.2.BaseDAO的概念:
基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,复用增删改查的基本操作,我们称为BaseDAO。
//新建一个DAO包,在该包中新增一个名为EmployeeDAO的接口
public interface EmployeeDAO {
List<Employee> selectAll();
Employee selectByEmpId(Integer empId);
int insert(Employee employee);
int update(Employee employee);
int delete(Integer empId);
}
//在该DAO包中新建一个impl子包,其中新增EmployeeDAO接口的实现类,实现具体的增删改查的操作
public class BaseDAO{
public int executeUpdate(String sql,Object... params){
Connection connection = JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null && params.length>0){
for(int i=0;i<params.length;i++){
//占位符是从1开始的,参数的数组是从0开始的
preparedStatement.setObject(i+1,params[i]);
}
}
int row = preparedStatement.executeUpdate();
preparedStatement.close();
JDBCUtilV2.release();
return row;
}
public <T> List<T> executeQuery(class<T> clazz,String sql,Object... params) throws Exception{
Connection connection = JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null && params.length>0){
for (int i=0;i<params.length;i++){
preparedStatement.setObject(i+1, params[i]);
}
}
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
List<T> list = new ArrayList<>();
while(resultSet.next()){
T t = clazz.newInstance();
for(int i=1;i <= columnCount;i++){
Object value = resultSet.getObject(i);
String fieldName = metaData.getColumnLabel(i);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(t,value);
}
list.add(t);
}
resultSet.close();
preparedStatement.close();
JDBCUtilV2.release();
return list;
}
}
//在该DAO包中新建一个impl子包,其中继承BaseDAO类,在实现EmployDAO中的方法实现具体的增删改查的操作
三、事务
3.1.事务回顾:
数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定。一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据。一个事务内任意一条语句失败,即为事务失败,我们可以触发rollback回滚结束事务,数据回到事务之前状态。
3.2.事务的特性:
(1)原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2)一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态;
(3)隔离性(lsolation):事务的隔离性是指一个事务的执行不能被其他事务干扰, 即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
(4)持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的, 接下来的其他操作和数据库故障不应该对其有任何影响;
3.3事务的提交方式:
(1)自动提交:每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚;
(2)手动提交:手动开启事务,添加语句,手动提交或者手动回滚即可
3.4.JDBC中实现事务
try{
connection.setAutoCommit(false);//关闭自动提交
//注意:只要当前connection对象进行数据库操作,都不会自动提交事务
//prepareStatement-单的数据库动作crud
//所有操作执行正确,提交事务
connection.commit();
}catch(Execption e){
//出现异常,则回滚事务!
connection.rollback();
}