import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//事物时,Service和dao属于同一线程,不用再传参数了
/*
这个工具类的作用就是用来给所有的SQL操作提供“连接”,和释放连接。
这里使用ThreadLocal的目的是为了让同一个线程,在多个地方getConnection得到的是同一个连接。
这里使用DataSource的目的是为了(1)限制服务器的连接的上限(2)连接的重用性等*/
public class JDBCTools {private static DataSource ds;private static ThreadLocal<Connection> tl = new ThreadLocal<>();static{//静态代码块,JDBCToolsVersion1类初始化执行try {Properties pro = new Properties();pro.load(ClassLoader.getSystemResourceAsStream("druid.properties"));ds = DruidDataSourceFactory.createDataSource(pro);} catch (Exception e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {Connection connection = tl.get();if(connection == null){//当前线程还没有拿过连接,就给它从数据库连接池拿一个connection = ds.getConnection();tl.set(connection);}return connection;}public static void free() throws SQLException {Connection connection = tl.get();if(connection != null){tl.remove();connection.setAutoCommit(true);//避免还给数据库连接池的连接不是自动提交模式(建议)connection.close();}}
}
基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,我们称为BaseDao
针对DQL查询和非DQL进行,分成两类
public abstract class BaseDao {/*通用的增、删、改的方法String sql:sqlObject... args:给sql中的?设置的值列表,可以是0~n*/protected int update(String sql,Object... args) throws SQLException {
// 创建PreparedStatement对象,对sql预编译Connection connection = JDBCTools.getConnection();PreparedStatement ps = connection.prepareStatement(sql);//设置?的值if(args != null && args.length>0){for(int i=0; i<args.length; i++) {ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始}}//执行sqlint len = ps.executeUpdate();ps.close();//这里检查下是否开启事务,开启不关闭连接,业务方法关闭!//没有开启事务的话,直接回收关闭即可!if (connection.getAutoCommit()) {//回收JDBCTools.free();}return len;}/*通用的查询多个Javabean对象的方法,例如:多个员工对象,多个部门对象等这里的clazz接收的是T类型的Class对象,如果查询员工信息,clazz代表Employee.class,如果查询部门信息,clazz代表Department.class,返回List<T> list*/protected <T> ArrayList<T> query(Class<T> clazz,String sql, Object... args) throws Exception {// 创建PreparedStatement对象,对sql预编译Connection connection = JDBCTools.getConnection();PreparedStatement ps = connection.prepareStatement(sql);//设置?的值if(args != null && args.length>0){for(int i=0; i<args.length; i++) {ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始}}ArrayList<T> list = new ArrayList<>();ResultSet res = ps.executeQuery();/*获取结果集的元数据对象。元数据对象中有该结果集一共有几列、列名称是什么等信息*/ResultSetMetaData metaData = res.getMetaData();int columnCount = metaData.getColumnCount();//获取结果集列数//遍历结果集ResultSet,把查询结果中的一条一条记录,变成一个一个T 对象,放到list中。while(res.next()){//循环一次代表有一行,代表有一个T对象T t = clazz.newInstance();//要求这个类型必须有公共的无参构造//把这条记录的每一个单元格的值取出来,设置到t对象对应的属性中。for(int i=1; i<=columnCount; i++){//for循环一次,代表取某一行的1个单元格的值Object value = res.getObject(i);//这个值应该是t对象的某个属性值//获取该属性对应的Field对象
// String columnName = metaData.getColumnName(i);//获取第i列的字段名String columnName = metaData.getColumnLabel(i);//获取第i列的字段名或字段的别名Field field = clazz.getDeclaredField(columnName);field.setAccessible(true);//这么做可以操作private的属性field.set(t, value);}list.add(t);}res.close();ps.close();//这里检查下是否开启事务,开启不关闭连接,业务方法关闭!//没有开启事务的话,直接回收关闭即可!if (connection.getAutoCommit()) {//回收JDBCTools.free();}return list;}protected <T> T queryBean(Class<T> clazz,String sql, Object... args) throws Exception {ArrayList<T> list = query(clazz, sql, args);if(list == null || list.size() == 0){return null;}return list.get(0);}
}