引言
作为一个初学jdbc的人,你一定会被大量的语句编码所烦恼,一个连接就得啪啪啪写上10多行语句就是单单的为了仅仅执行一条sql语句,然后从结果集中写上了大量的get方法,苦的一笔啊~今天看了我这个,你讲告别烦恼,以后只需要使用简简单单的一条语句就行,其它框架帮你搞定。ps其实就是dbUtils的简单自制版
所用知识
- 数据库连接池: druid,这个是我直接用的现成的,功能很强大,这边有我总结的简单介绍https://blog.csdn.net/qq_41376740/article/details/81869261
- 泛型方法
- 策略设计模式
- 解决LocalDate和sql.Date转换问题
- 反射
- 可变参数
设计思路
设计结构:
设计图示:
所用依赖:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.9</maven.compiler.source>
<maven.compiler.target>1.9</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
</dependencies>
<!--build中添加-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.properties</include>
</includes>
</resource>
</resources>
整体代码
写在前面:这个框架有一个缺陷,就是bean的属性和数据库中的需要高度一致,看了下面的源代码,你会发现,这个是反射读取bean的fields然后再读resultSet结果集的。
数据表结构:
模型bean:
package bean;
import java.time.LocalDate;
public class Student {
private int id;
private String name;
private LocalDate birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
}
不难看出我们将数据库的表的字段名称和实体类的field设置成了一致,之所以使用LocalDate,是因为它是1.8新出的东西,操作日期,线程安全等好处都是比较强大的。可以参考我这篇文章:https://blog.csdn.net/qq_41376740/article/details/81292504
核心包:
核心包的内容主要有这样的几块
- 静态代码块初始化数据连接池,从properties文件中直接读取数据库的配置。
- 获取,释放连接。这里我抽取了重复的代码,用重载实现几种释放资源
- update用来执行增删改的操作
- query重载方法,实现单一和多个查找的功能
- 由于两个query有相同的封装bean的方法,所以我抽取出来形成了rs2Bean,就是将结果集封装到bean里面
package Utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import handlers.Handler;
import org.apache.commons.beanutils.BeanUtils;
import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* @author 肖鑫
*/
public class JDBCUtils {
private static DataSource dataSource = null;
static {
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");
Properties properties = new Properties();
try {
properties.load(in);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @return 返回从连接池中拿的连接的连接
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 关闭连接,释放资源
*
* @param conn 使用的连接
*/
private static void release(Connection conn) {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException("关闭conn时异常", e);
}
}
private static void release(Statement st) {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
throw new RuntimeException("关闭statement时异常", e);
}
}
private static void release(ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
throw new RuntimeException("关闭resultSet时异常", e);
}
}
private static void release(Connection conn, Statement st) {
release(conn);
release(st);
}
private static void release(Connection con, Statement st, ResultSet rs) {
release(con);
release(st);
release(rs);
}
/**
* 用于结果集rs->bean的转换
*
* @param rs 结果集
* @param clazz 传递进去的bean,为了适应不同的bean
* @param <T> 泛型参数
* @return 返回结果集中封装后的实例化的对象
* @throws Exception
*/
public static <T> T rs2Bean(ResultSet rs, Class<T> clazz) throws Exception {
Field[] fields = clazz.getDeclaredFields();
return createBean(rs, fields, clazz);
}
/**
* 将结果集中的多个数据全部封装成bean然后存储进list里面
*
* @param rs 结果集
* @param clazz 传递进去的bean,为了适应不同的bean
* @param <T> 泛型参数
* @return list集合
* @throws Exception
*/
public static <T> List<T> rs2BeanList(ResultSet rs, Class<T> clazz) throws Exception {
Field[] fields = clazz.getDeclaredFields();
List<T> list = new ArrayList<>();
while (rs.next()) {
T bean = createBean(rs, fields, clazz);
list.add(bean);
}
return list;
}
/**
* rs->bean的转换,用的是反射,单独抽取出来,是避免while循环中多次getDeclaredFields
* @throws Exception
*/
private static <T> T createBean(ResultSet rs, Field[] fields, Class<T> clazz) throws Exception {
T bean = clazz.getConstructor().newInstance();
for (Field field : fields) {
String name = field.getName();
Object object = rs.getObject(name);
if (object instanceof Date) {
object = ((Date) object).toLocalDate();
}
BeanUtils.setProperty(bean, name, object);
}
return bean;
}
/**
* @param sql 需要执行的sql语句
* @param paras 预编译中的?参数组成的一个可变数组
*/
public static void update(String sql, Object... paras) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JDBCUtils.getConnection();
st = conn.prepareStatement(sql);
for (int i = 0; i < paras.length; i++) {
Object obj = paras[i];
if (obj instanceof LocalDate) {
Date date = Date.valueOf((LocalDate) obj);
st.setObject(i + 1, date);
continue;
}
st.setObject(i + 1, obj);
}
st.executeUpdate();
} catch (Exception e) {
throw new RuntimeException("更新错误", e);
} finally {
JDBCUtils.release(conn, st);
}
}
/**
*实现单个搜索功能
*/
public static <T> T query(String sql, Handler<T> handler, Object... params) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
st = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
st.setObject(i + 1, params[i]);
}
rs = st.executeQuery();
return handler.handle(rs);
} catch (Exception e) {
throw new RuntimeException("查询异常", e);
} finally {
JDBCUtils.release(conn, st, rs);
}
}
/**
* 执行搜索全部的功能
*/
public static <T> List<T> queryAll(String sql, Handler<List<T>> handler) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
st = conn.prepareStatement(sql);
rs = st.executeQuery();
return handler.handle(rs);
} catch (Exception e) {
throw new RuntimeException("查询异常", e);
} finally {
JDBCUtils.release(conn, st, rs);
}
}
}
使用策略模式,来实现多个handle行为
定义接口:
package handlers;
import java.sql.ResultSet;
public interface Handler<T> {
T handle(ResultSet rs) throws Exception;
}
两个实现:
package handlers;
import Utils.JDBCUtils;
import java.sql.ResultSet;
public class BeanHandler<T> implements Handler<T> {
private Class<T> clazz;
public BeanHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T handle(ResultSet rs) throws Exception {
return rs.next() ? JDBCUtils.rs2Bean(rs, this.clazz) : null;
}
}
package handlers;
import Utils.JDBCUtils;
import java.sql.ResultSet;
import java.util.List;
public class BeanListHandler<T> implements Handler<List<T>> {
private Class<T> clazz;
public BeanListHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public List<T> handle(ResultSet rs) throws Exception {
if (rs == null) {
return null;
}
List<T> list = JDBCUtils.rs2BeanList(rs, this.clazz);
return list.size() == 0 ? null : list;
}
}
这个使用的是策略模式,我们定义了一个接口约定了行为,对于这个行为我们可以用多个不同的类来执行不同的处理手段,这样我们只需要根据需求new出实现类的实例,就可以使用接口定义的方法使用不同的处理方法。这里使用的是两个,一个是将查询的单条数据封装成单个bean,一个是将查询的多条数据封装成一个list。
测试代码
@Test
public void testUpdate() {
LocalDate date = LocalDate.now();
LocalDate date1 = LocalDate.of(1997, 1, 31);
String sql = "insert into student(name,birthday) values(?,?)";
JDBCUtils.update(sql, "肖鑫", date);
String sql2 = "update student set birthday=? where name=?";
JDBCUtils.update(sql2, date1, "肖鑫");
}
@Test
public void testQuery() {
String sql = "select * from student where id=?";
BeanHandler<Student> handler = new BeanHandler<>(Student.class);
Student student = JDBCUtils.query(sql, handler, 1);
}
@Test
public void testQueryALl() {
String sql = "select * from student";
BeanListHandler<Student> handler = new BeanListHandler<>(Student.class);
List<Student> list = JDBCUtils.queryAll(sql, handler);
}
测试结果:
QueryAll:
Query:
数据库
原来的数据是2018-08-20,完成了更新的操作,并且插入了数据.
总结
这次学习,是一次不小的提升,用到了泛型,反射,平时我很少用到泛型,写泛型的时候总是那么尴尬,不知道怎么写,到处报错,泛型类和泛型方法,泛型接口怎么写,算是好好复习了一下。运用到了实践当中,然后就是策略模式的运用,原来这就是策略,不同的处理类实现同一个类。妙哉妙哉~很满足,很过瘾