5.3jdbc3.0存在的问题
我们可以看到,对于增删改方法返回的是受影响的行数,我们可以用一个通过的方法解决,但是对于查询,我们的返回值可能多种多样,这时候就要考虑数据类型的多样性,这时候我们就可以用到泛型了。
5.4元数据
分为数据库元数据,执行者元数据,结果集元数据
@Test
public void testMetaData() throws SQLException {
Connection connection = JdbcUtils.getConnection();
//数据库元数据 connection.getMetaData
DatabaseMetaData metaData = connection.getMetaData();
System.out.println("URL:"+metaData.getURL());
System.out.println("DriverVersion:"+metaData.getDriverMajorVersion());
System.out.println("UserName:"+metaData.getUserName());
System.out.println();
String sql = "select * from user where id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setObject(1,1);
//可以通过PreparedStatement 执行者对象获取参数元数据对象
ParameterMetaData parameterMetaData = statement.getParameterMetaData();
//sql语句中的参数个数
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("SQL语句对应的参数:"+parameterCount);
System.out.println();
ResultSet resultSet = statement.executeQuery();
//【VIK】 结果集元数据,需要从结果集中获取【字段名称,字段个数】
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
int countmnCount = resultSetMetaData.getColumnCount();
System.out.println("结果集字段个数:"+countmnCount);
for (int i = 1; i <= countmnCount; i++) {
System.out.println("字段名/列名:"+resultSetMetaData.getColumnName(i));
}
connection.close();
}
5.5BeanUtils 和DbUtils工具类
1 DbUtils工具类
Apache组织下的一个轻量版ORM框架 * 两个核心方法 * update * query * 一个核心类 QueryRunner类 QueryRunner runner = new QueryRunner(); // 增删改都是执行一个方法,返回的参数是一个受影响的行数 runner. update(connection,sql,parameters); // 第一个参数表示数据库连接对象,第二个参数表示sql语句,第三个参数表示sql语句中的参数,用Object[]的数组来完成 // 查询的返回值多种多样,因此我们地选择适合的处理器类型返回返回对应的数据类型 runner.query(connection.sql, 处理器类型,parameters); 处理器类型主要有六个: new BeanHandler()<Class<?> cls> 返回一个Bean对象,用到反射,适合查询一行数据 new BeanListHandler()<Class<?> cls> 返回一个List<Bean>集合,用到反射,适合查询整张表 new ArrayHandler() 返回一个Object[]的数组,适合查询一行数据 new ArrayListHandler() 返回一个List<Object[]>的数组,适合整张表 new MapHandler() 返回一个Map<String,Object>的双边队列,适合查询一行数据 new MapListHandler() 返回一个List<Map<String,Object>>的List集合,适合查询一整张表。
2 BeanUtils 工具类
提供了对于符合javaBan规范的实体类进行赋值,取值,拷贝操作,可以自动完成类型数据转换,常用的三个静态方法
BeanUtils.setProperty(Bean,field,value)
BeanUtils.getProperty(Bean,field)
BeanUtils.copyProperties(Bean1,Bean2)
HashMap<String,Object> map = new HashMap<>();
BeanUtils.populate(Bean1,map)
6.jdbc4.0版本
得到一个PreparedStatemente对象,并且对通用的为sql语句参数赋值的方法
/**
* 获取数据库执行者对象
* @param sql sql语句
* @param connection 连接对象
* @param parameters 参数列表
* @return 返回一个PreparedStatement对象
*/
public static PreparedStatement handlerPrepareStatement(String sql, Connection connection, Object... parameters) {
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
//为预编译的sql语句赋值
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (parameterCount > 0 && parameters != null && parameters.length == parameterCount) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i+1,parameters[i]);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return statement;
}
BaseDao中六个通用的查询方法并且返回值不同的类型,满足数据查询的多样性,
一 返回值为QueryBean
/**
* 查询结果集返回一个Bean对象
* @param sql 执行的sql
* @param cls 反射对象
* @param parameters sql语句中的参数
* @param <T> 自定义泛型占位符
* @return 返回一个T类型的数据
*/
public <T> T queryBean(String sql, Class<T> cls, Object... parameters) {
T t = null;
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet;
try {
//获取数据库连接对象
connection = JdbcUtils.getConnection();
//获取数据库连接对象
statement = handlerPrepareStatement(sql, connection, parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
if (resultSet.next()) {
t = cls.getConstructor().newInstance();
for (int i = 0; i < columnCount; i++) {
BeanUtils.setProperty(t,metaData.getColumnName(i+1),resultSet.getObject(i+1));
}
}
} catch (SQLException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,connection);
}
return t;
}
二返回值为QueryBeanList
/**
* 查询数据库中的一张表将数据返回成List集合
* @param sql 执行的sql语句
* @param cls 泛型约束的具体类型
* @param parameters sql语句中的参数
* @param <T> 自定义泛型
* @return 返回值是一个List集合
*/
public <T> List<T> queryBeanList(String sql, Class<T> cls, Object... parameters) {
// 1. 准备必要参数
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
ArrayList<T> list = new ArrayList<>();
// 2. 获取数据库连接
try {
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql,connection,parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
T t = cls.getConstructor().newInstance();
for (int i = 0; i < columnCount; i++) {
BeanUtils.setProperty(t,metaData.getColumnName(i+1),resultSet.getObject(i+1));
}
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement, resultSet);
}
return list.size() > 0 ? list : null;
}
三返回值为Object类型的数组
public Object[] queryObject(String sql,Object...parameters) {
// 1. 准备必要参数
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
Object[] obj = null;
// 2. 获取数据库连接
try {
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql, connection, parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
if (resultSet.next()) {
obj = new Object[columnCount];
for (int i = 0; i < columnCount; i++) {
obj[i] = resultSet.getObject(i + 1);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,resultSet);
}
return obj;
}
四 返回List<Object[]>类型
/**
* 通用查询方法
* @param sql 指定的sql语句
* @param parameters sql语句的参数
* @return 指定的返回值
*/
public List<Object[]> queryObjectList(String sql,Object...parameters) {
// 1. 准备必要数据
// 1. 准备必要参数
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Object[]> list = new ArrayList<>();
// 2. 获取数据库连接
try {
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql, connection, parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Object[] obj = new Object[columnCount];
for (int i = 0; i < columnCount; i++) {
obj[0] = resultSet.getObject(i+1);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,resultSet);
}
return list;
}
五 返回Map<String ,Object>类型
public Map<String, Object> queryMap(String sql, Object...parameters) {
// 1. 准备必要的资源
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
HashMap<String, Object> map = new HashMap<>(4);
try {
//获取数据库的三个常用对象
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql, connection, parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
if (resultSet.next()) {
for (int i = 0; i < columnCount; i++) {
map.put(metaData.getColumnName(i + 1), resultSet.getObject(i + 1));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,resultSet);
}
return map;
}
六返回值是List<Map<String,Object>
public List<Map<String, Object>> queryMapList(String sql, Object...parameters) {
// 1. 准备必要的资源
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Map<String, Object>> list = new ArrayList<>();
try {
//获取数据库的三个常用对象
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql, connection, parameters);
resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Map<String,Object> map = new HashMap<>(4);
for (int i = 0; i < columnCount; i++) {
map.put(metaData.getColumnName(i + 1), resultSet.getObject(i + 1));
}
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,resultSet);
}
return list;
}
测试代码:
@Test
public void beanTest() {
String sql = "select * from student where id = ?";
Student student = super.queryBean(sql, Student.class,1);
System.out.println(student);
}
@Test
public void beanListTest() {
String sql = "select * from student ";
List<Student> students = super.queryBeanList(sql, Student.class);
System.out.println(students);
}
@Test
public void objecTest() {
String sql = "select * from student where id = ?";
Object[] objects = super.queryObject(sql, 1);
System.out.println(Arrays.toString(objects));
}
@Test
public void listObjectTest() {
String sql = "select * from student ";
List<Object[]> list = super.queryObjectList(sql);
System.out.println(list);
}
@Test
public void mapTest() {
String sql = "select * from student where id = 1";
Map<String, Object> map = super.queryMap(sql, 1);
System.out.println(map);
}
@Test
public void mapListTest() {
String sql = "select * from student ";
List<Map<String, Object>> map = super.queryMapList(sql, 1);
System.out.println(map);
}
至此,BaseDao已经具备通用的更新方法,以及有六种返回值类型的查询方法。
我们可以注意到的是,这时候我们的查询的时候步骤都是一样的,只有返回的数据类型不一致,这时候六个查询方法之间就产生了冗余,所以我们还能继续简化。
7.jdbc5.0版本
专门负责一个接收不同返回返回值的接口处理器,创建六个子类去继承这个返回值处理器,关注的重点只有查询的返回值类型,可以理解为DbUtils的源码实现
ResultSetHandler接口
@FunctionalInterface
public interface ResultSetHandler<T> {
/**
* 结果集处理规范方法,用于处理java.sql,ResultSet查询结果集
* @param res 查询结果集对象
* @return 约束泛型,由实现类约束泛型
* @throws SQLException SQL异常
*/
T handle(ResultSet res) throws SQLException;
}
将六个处理器类继承这个接口,根据不同的返回值类型返回不同处理器,实现解耦合。
BeanListHandler
public class BeanListHandler<T> implements ResultSetHandler<List<T>> {
/** 利用class类的处死话操作确定泛型的值
*
*/
//获取指定类类型对象
private final Class<T> cls;
public BeanListHandler(Class<T> cls) {
this.cls = cls;
}
@Override
public List<T> handle(ResultSet res) throws SQLException {
// 1 准备必要参数
List<T> list =new ArrayList<>();
ResultSetMetaData metaData = res.getMetaData();
int columnCount = metaData.getColumnCount();
while (res.next()) {
try {
T t = cls.getConstructor().newInstance();
for (int i = 0; i < columnCount; i++) {
BeanUtils.setProperty(t,metaData.getColumnName(i+1),res.getObject(i+1));
}
list.add(t);
} catch (Exception e) {
e.printStackTrace();
}
}
return list;
}
}
BeanHandler(可以利用BeanListhandler方法)
public class BeanHandler<T> implements ResultSetHandler<T> {
private final Class<T> cls;
public BeanHandler(Class<T> cls) {
this.cls = cls;
}
@Override
public T handle(ResultSet res) throws SQLException {
List<T> list = new BeanListHandler<>(cls).handle(res);
return list != null ? list.get(0) : null;
}
}
ArrayListHandler
public class ArrayListHandler implements ResultSetHandler<List<Object[]>> {
@Override
public List<Object[]> handle(ResultSet res) throws SQLException {
ResultSetMetaData metaData = res.getMetaData();
int columnCount = metaData.getColumnCount();
List<Object[]> list = new ArrayList<>();
while (res.next()) {
Object[] objects = new Object[columnCount];
for (int i = 0; i < columnCount; i++) {
objects[i] = res.getObject(i+1);
}
list.add(objects);
}
return list;
}
}
ArrayHandler
public class ArrayHandler implements ResultSetHandler<Object[]> {
@Override
public Object[] handle(ResultSet res) throws SQLException {
List<Object[]> handle = new ArrayListHandler().handle(res);
return handle != null ? handle.get(0) : null;
}
}
MapListHandler
public class MapListHandler implements ResultSetHandler<List<Map<String,Object>>> {
@Override
public List<Map<String, Object>> handle(ResultSet res) throws SQLException {
ResultSetMetaData metaData = res.getMetaData();
int columnCount = metaData.getColumnCount();
List<Map<String, Object>> list = new ArrayList<>();
while (res.next()) {
Map<String, Object> map = new HashMap<>(4);
for (int i = 0; i < columnCount; i++) {
map.put(metaData.getColumnName(i + 1), res.getObject(i + 1));
}
list.add(map);
}
return list;
}
}
MapHandler
public class MapHandler implements ResultSetHandler<Map<String,Object>> {
@Override
public Map<String, Object> handle(ResultSet res) throws SQLException {
List<Map<String, Object>> map = new MapListHandler().handle(res);
return map != null ? map.get(0) : null ;
}
}
此时我们BaseDao可以改造成这样了。
BaseDao类
public <T> T queryBean(String sql, Class<T> cls, Object... parameters) {
return query(sql,new BeanHandler<>(cls),parameters);
}
public <T> List<T> queryBeanList(String sql, Class<T> cls, Object... parameters) {
return query(sql,new BeanListHandler<>(cls),parameters);
}
public Object[] queryObject(String sql, Object... parameters) {
return query(sql,new ObjectHandler(),parameters);
}
public List<Object[]> queryObjectList(String sql, Object... parameters) {
return query(sql, new ObjectListHandler(), parameters);
}
public Map<String, Object> queryMap(String sql,Object...parameters) {
return query(sql, new MapHandler(), parameters);
}
public List<Map<String, Object>> queryMapList(String sql, Object... paramerters) {
return query(sql, new MapListHandler(), paramerters);
}
/**
* 通用查询方法
* @param sql sql语句
* @param rsh 处理结果集接口实现类
* @param parameters sql语句中的参数
* @param <T> 自定义泛型T
* @return 返回自定义泛型
*/
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... parameters) {
//1 准备必要变量
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
T t = null;
try {
connection = JdbcUtils.getConnection();
statement = handlerPrepareStatement(sql,connection,parameters);
resultSet = statement.executeQuery();
t = rsh.handle(resultSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection,statement,resultSet);
}
return t;
}
// 获取赋值完毕PreparedStatement对象
public static PreparedStatement handlerPrepareStatement(String sql, Connection connection, Object... parameters) {
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
//为预编译的sql语句赋值
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (parameterCount > 0 && parameters != null && parameters.length == parameterCount) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i+1,parameters[i]);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return statement;
}
代码执行:
public class SelectTwoTest extends BaseDao {
@Test
public void beanTest() {
String sql = "select * from student where id = 2";
Student student = super.queryBean(sql, Student.class, 1);
System.out.println(student);
}
@Test
public void beanListTest() {
String sql = "select * from student ";
List<Student> students = super.queryBeanList(sql, Student.class, 1);
System.out.println(students);
}
@Test
public void ObjectTest() {
String sql = "select * from student where id = ?";
Object[] objects = super.queryObject(sql, 1);
System.out.println(Arrays.toString(objects));
}
@Test
public void objectListTest() {
String sql = "select * from student ";
List<Object[]> objects = super.queryObjectList(sql, 1);
System.out.println(objects);
}
@Test
public void mapTest() {
String sql = "select * from student where id = ?";
Map<String, Object> map = super.queryMap(sql, 1);
System.out.println(map);
}
@Test
public void mapListTset() {
String sql = "select * from student";
List<Map<String, Object>> list = super.queryMapList(sql);
System.out.println(list);
}
至此我们已经完成通用增删改查的全部方法,并且高度实现了解耦合,相当于自己写了个小框架
8 .jdbc 6.0版本
使用数据库连接池Druid节省系统资源 和使用QueryRunner核心类替换六个执行器类。至此,我们jdbc有繁琐到简便功能基本实现。为了节省代码空间,我们这个时候就可以使用别人的写好的小框架了讲话代码
使用数据库连接池
db.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/acxng?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=root
initialSize=5
maxActive=20
maxWait=2000
JdbcUtils工具类变化
public class JdbcUtil {
/**
* 数据库资源
*/
private static DataSource ds;
static {
try {
// 加载Properties文件
Properties properties = new Properties();
/*
这是一个WEB项目,资源都是按照WEB所需资源在对应的路径中,而不是原始可见的路径关系
Java文本都是编译生成对应的.class字节码文件放到WEB 服务器中运行.
JdbcUtil.class JdbcUtil类对应的Class对象
getClassLoader 类加载器,帮助我们完成.class加载到内存中
getResourceAsStream 获一个指定资源的InputStream输入字节流对象
getClassLoader().getResourceAsStream(String sourceFile)
当前.class所处位置帮助你搜索并且得到对应的Stream类
*/
properties.load(JdbcUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
// 已经完成数据库连接池操作
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 返回数据库连接对象,连接失败返回null
*
* @return java.sql.Connection 数据库连接对象
*/
public static Connection getConnection() {
Connection connection = null;
try {
connection = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
/*
以下三个方法实际上都是执行同一个方法,使用这种方式
1. 简化代码结构
2. 规范化所有的操作
*/
/**
* 处理数据库操作对应的资源问题
*
* @param connection java.sql.Connection 数据库连接对象
*/
public static void close(Connection connection) {
close(connection, null, null);
}
/**
* 处理数据库操作对应的资源问题
*
* @param connection java.sql.Connection 数据库连接对象
* @param statement java.sql.Statement 数据库SQL语句搬运工对象
*/
public static void close(Connection connection, Statement statement) {
close(connection, statement, null);
}
/**
* 处理数据库操作对应的资源问题
*
* @param connection java.sql.Connection 数据库连接对象
* @param statement java.sql.Statement 数据库SQL语句搬运工对象
* @param resultSet java.sql.ResultSet 数据库查询结果集对象
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
BaseDao工具类的变化(此时ResultSetHandler接口处理器和子类都不负存在了,Jdbc的工具类就剩两个了)
public class BaseDao {
/**
* 通用的更新方法,需要参数是SQL语句和对应当前SQL语句的Object类型参数数组
*
* @param sql String类型的SQL语句,需要执行的方法
* @param parameters 对应当前SQL语句的参数列表。Object类型数组
* @return SQL语句执行数据库受到影响的行数
*/
public int update(String sql, Object[] parameters) {
// DbUtils核心类,QueryRunner
QueryRunner runner = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
int affectedRows = 0;
try {
affectedRows = runner.update(connection, sql, parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection);
}
return affectedRows;
}
/**
* 通用的查询方法,查询cls指定数据类型,单一对象
*
* @param sql Select SQL语句
* @param parameters 对应当前SQL语句的参数
* @param cls 指定数据类型,同时提供Class对象,便于里用反射操作,约束泛型类型
* @param <T> 泛型占位符
* @return 单一指定类型对象,没有找到返回null
*/
public <T> T queryBean(String sql, Object[] parameters, Class<T> cls) {
// DbUtils核心类,QueryRunner
QueryRunner runner = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
T t = null;
try {
t = runner.query(connection, sql, new BeanHandler<>(cls), parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection);
}
return t;
}
/**
* 通用的查询方法,查询cls指定数据类型,包含指定对象的List集合
*
* @param sql Select SQL语句
* @param parameters 对应当前SQL语句的参数
* @param cls 指定数据类型,同时提供Class对象,便于里用反射操作,约束泛型类型
* @param <T> 泛型占位符
* @return 返回包含指定对象的List集合,没有数据返回null
*/
public <T> List<T> queryBeanList(String sql, Object[] parameters, Class<T> cls) {
// DbUtils核心类,QueryRunner
QueryRunner runner = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
List<T> list = null;
try {
list = runner.query(connection, sql, new BeanListHandler<>(cls), parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection);
}
return list;
}
/**
* 通用查询方法,返回值是对应字段数据的Object类型数组,并且存储于List集合
*
* @param sql Select查询SQL语句
* @param parameters 对应当前SQL语句的参数
* @return 包含数据行数据的List<Object[]> 如果没有查询到数据,返回null
*/
public List<Object[]> queryArrayList(String sql, Object[] parameters) {
QueryRunner runner = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
List<Object[]> list = null;
try {
list = runner.query(connection, sql, new ArrayListHandler(), parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection);
}
return list;
}
public Map<String,Object> queryMap(String sql, Object[] parameters) {
QueryRunner runner = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
Map<String,Object> map = null;
try {
map = runner.query(connection, sql, new MapHandler(), parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection);
}
return map;
}
}