简单的DbUtils工具类【精细】

目录

单条通用增删改方法

        1.创建maven项目,并加载依赖

 2.创建数据库连接工具类(Dbutils类)

3.创建一个执行器(SqlExecutor类)

4.通用(增,删,改)方法

1.创建方法

2.创建userInfo实体类

3.创建测试类,测试增,删,改三个操作

4.最后执行结果

批量(增,删,改)方法

1.在sqlExecutor类中添加一个executeBatch(String,Object[][])方法

2. 测试类中执行 批量(增,删,改)操作

3.最后运行结果

通用查询

策略模式:

1.定义一个类型转换接口 ResultSetHandler

【一】将查询结果转换为Array数组

1.定义ArrayHandler转换类,实现ResultSetHandler接口

2.定义RowProcessor行处理器

3. 在SqlExecutor类中定义通用查询方法

【二】将查询结果转换为Map集合

1.定义MapHandler类,实现ResultSetHandler

2.在RowProcessor类中实现具体转换map集合的方法

【二.五】测试转换的两个类型(Array和Map)

1.分别创建两个策略

2.最后转换结果

【三】将结果集转换成Bean对象

1.创建一个Cloumn注解【映射数据库名称】

2. 在实体类中加上自定义Column注解

3.创建一个BeanHandler策略类实现ResultSetHandler接口

4.在行RowProcessor行处理器工具类添加实现转换bean对象的方法

getPropertyDescriptors(Class);

newInstance(Class clazz);

checkColumn(String,PropertyDescriptor, Class);

setBean(String,ResultSet,PropertyDescriptor, T);

5.最后在测试类中进行测试结果

6. 最后执行结果


单条通用增删改方法

        1.创建maven项目,并加载依赖

对应maven的依赖为:

        <!--mysql驱动--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.2.0</version></dependency><!--单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!--lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version><scope>provided</scope></dependency>

 2.创建数据库连接工具类(Dbutils类)

作用:   用于获取Connection连接对象,连接数据库

package com.xr.utils;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;/*** 数据库连接工具类* 作用: 用于获取Connection连接对象* @author xr* @Date 2024/5/8 13:10*/public class Dbutils {/*** 数据库驱动* @Date 2024/5/8 13:12*/private static final String DRIVER = "com.mysql.cj.jdbc.Driver";/*** 数据据连接地址* jdbc:mysql://localhost:3306/数据库名称?serverTimezone=GMT%2B8"* @Date 2024/5/8 13:12*/private static final String URL = "jdbc:mysql://localhost:3306/dbutils?serverTimezone=GMT%2B8";/*** 数据库用户名* @Date 2024/5/8 13:12*/private static final String USER = "root";/*** 数据库密码* @Date 2024/5/8 13:12*/private static final String PASSWORD = "1234";/*** 获取数据库连接* @Date 2024/5/8 13:12*/public static Connection getConnection() {// 定义数据库连接对象Connection conn = null;try {// 注册JDBC驱动Class.forName(DRIVER);// 获取数据库连接conn = DriverManager.getConnection(URL, USER, PASSWORD);} catch (ClassNotFoundException | SQLException e) {throw new RuntimeException("数据数据库连接失败!!!!",e);}// 返回连接对象return conn;}
}

3.创建一个执行器(SqlExecutor类)

作用: 

         封装增,删,查,改操作

package com.xr.utils;import java.sql.Connection;/*** sql 执行器,操作执行sql,通用增删改操作* 作用: 封装增删改查操作* @author xr* @Date 2024/5/8 13:19*/
public class SqlExecutor {/*** 定义连接对象* @Date 2024/5/8 13:20*/private final Connection conn;/*** 构造方法,初始化连接对象* @Date 2024/5/8 13:20*/public SqlExecutor(Connection conn) {this.conn = conn;}}


4.通用(增,删,改)方法

因为我想使用一个方法就可以完成(增,删,改)操作

1. 例如:

        // 增加数据

        insert into user_info(u_name,u_password,u_age,deposit,brith)

        value(?,?,?,?,?);

        // 删除数据

        delete from user_info where u_name = ?

        // 修改数据

        update user set u_name = ? where id = ?

2. 发现:

        我们的有不同的sql语句,设置不同的值

3. 解决方案: 

        使用PreparedStatement中的预编译sql语句,使用占位数 ? 解决值未知问题

4.思路: 

         1. 定义方法 excuteUpdate(sql,Object... params){};

         2. sql: sql语句  , params : 为多个参数,返回一个任意类型数组

         3. 将sql语句进行预编译操作,设置传递过来的参数

         4. 为什么要将预编译操作抽离出来一个方法,因为可以使方法结果更清晰

         5. 执行sql语句,返回受影响行数

         6.关闭资源

1.创建方法

        executeUpdate(String,Object...) : 通用增,删,改方法

        setPreparedStatement(PreparedStatement,Object...) : sql语句预编译,并设置参数

        close(Statement st) :关闭Statement对象资源

        close(ResultSet rs) : 关闭ResultSet对象资源

        close() :关闭Connection连接对象资源

    /*** 通用执行增删改操作方法* sql: sql语句* params: sql语句中占位符对应的参数* @Date 2024/5/8 13:23*/    
public int executeUpdate(String sql,Object... params) throws SQLException {// 判断连接对象是否为空if (conn == null) {throw new RuntimeException("连接对象为空!!!");}// 判断sql语句是否为空if(sql == null || sql.isEmpty()) {throw new RuntimeException("sql语句为空!!!");}// 定义PreparedStatement对象,作用对sql语句进行预编译PreparedStatement ps = null;try {// 将sql进行预编译,方法中抛出SQL异常ps = conn.prepareStatement(sql);// 设置sql 中 ?号 占位符对应的参数// 比如 sql = "insert into user(name,age) values(?,?)"setPreparedStatementParams(ps,params);// 执行sql语句,返回影响行数return ps.executeUpdate();} catch (SQLException e) {// 异常重抛,重抛成运行时异常throw new RuntimeException("执行sql语句失败!!!",e);} finally {// 关闭资源close(ps);close();}}/*** 设置sql预编译参数* @Date 2024/5/8 9:04*/private void setPreparedStatement(PreparedStatement preparedStatement,Object... params)throws SQLException {// insert into user(id,name,age) values(?,?,?)// update user set name=?,age=? where id=?// 循环的边界为传递过来的params参数长度for (int i = 0; i < params.length; i++) {// 设置占位符?对应的值,占位符下标从1开始preparedStatement.setObject(i+1,params[i]);}}/*** 关闭PreparedStatement对象资源* @Date 2024/5/8 13:54*/private void close(PreparedStatement ps) {if (ps != null) {try {// 关闭PreparedStatement对象资源ps.close();} catch (SQLException e) {throw new RuntimeException("关闭PreparedStatement对象失败!!!",e);}}}/*** 关闭连接对象资源* @Date 2024/5/8 13:54*/private void close() {if (conn != null ) {try {// 关闭连接对象资源conn.close();} catch (SQLException e) {throw new RuntimeException("关闭conn对象资源失败!!!",e);}}}/*** 关闭ResultSet对象资源* @param rs 结果集对象*/private void close(ResultSet rs) {if (rs != null) {try {// 关闭ResultSet对象资源rs.close();} catch (SQLException e) {throw new RuntimeException("关闭ResultSet对象失败!!!",e);}}}

2.创建userInfo实体类

package com.xr.entity;import com.xr.util.interfaceUtils.Column;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;
import java.time.LocalDateTime;/*** userInfo实体* @Data注解: 拥有get,set方法* @AllArgsConstructor: 拥有全参数构造方法* @NoArgsConstructor: 拥有无参构造方法* @author xr* @Date 2024/5/8 20:51*/@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {/*** id*/private Integer id; /*** 用户名*/private String username;/*** 密码*/private String password;/*** 年龄*/private Integer age;/*** 存款*/private BigDecimal deposit;/*** 生日*/private LocalDateTime birth;
}

3.创建测试类,测试增,删,改三个操作

package com.xr;import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.math.BigDecimal;
import java.sql.SQLException;
import java.time.LocalDateTime;/*** 测试类* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {//*----------------------添加数据-----------------------*/// 创建SqlExecutor对象控制器,并传入连接对象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[插入数据]String sql = "insert into user_info(u_name,u_password,u_age,deposit,birth) values(?,?,?,?,?)";// 增加数据操作int i = sqlExecutor.executeUpdate(sql, "张三", "123456", 18, new BigDecimal(1000), LocalDateTime.now());//输出结果System.out.println("插入数据:" + i +"行");/*----------------------修改数据-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[修改数据]sql = "update user_info set u_password = ? where u_name = ?";// 修改数据操作i = sqlExecutor.executeUpdate(sql, "22222","张三");//输出结果System.out.println("修改数据:" + i +"行");/*----------------------删除数据-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[删除数据]sql = "delete from user_info where u_name = ?";// 删除数据操作i = sqlExecutor.executeUpdate(sql, "张三");//输出结果System.out.println("删除数据:" + i +"行");}
}

4.最后执行结果


批量(增,删,改)方法

前面定义的方法是 :

        1.单条增加数据

        2.单条修改数据

        3.单条删除数据

现在我想批量做这些操作(多条),就是将多个sql语句参数装数组里面,定义一个Object[][]二维数组,存储参数

思路:

        1.将sql语句进行预编译

        2.将sql语句中的占位符对应的参数设置到sql语句中

        3.将sql语句添加到批量缓存中

        4.批量提交到数据库中

1.在sqlExecutor类中添加一个executeBatch(String,Object[][])方法

String : sql语句

Object[][] : sql语句的参数

  /*** 批量执行sql语句(增,删,改)* 实现思路:* 1.将sql语句进行预编译* 2.将sql语句中的占位符对应的参数设置到sql语句中* 3.将sql语句添加到批量缓存中* 4.批量提交到数据库中* @param sql sql语句* @param params 参数数组* @return 受影响行数*/public int[] executeBatch(String sql,Object[][] params) {if (conn == null) {throw new RuntimeException("连接对象为空!!!");}if (sql == null || sql.isEmpty()) {throw new RuntimeException("sql语句为空!!!");}// 定义PreparedStatement对象PreparedStatement ps = null;try {// 将sql语句进行预编译ps = conn.prepareStatement(sql);// 遍历params数组,将数组中的参数设置到sql语句中// 第一个循环遍历params数组,第二个循环遍历params数组中的数组for(Object[] param : params) {// 设置sql语句中占位符对应的参数setPreparedStatementParams(ps,param);//将ps的操作缓存到内存中,最后在批量提交到数据库中  添加到批量缓存中ps.addBatch();}// 批量提交sql语句return ps.executeBatch();} catch (SQLException e) {throw new RuntimeException("预编译sql语句失败!!!",e);} finally {close(ps);close();}}

2. 测试类中执行 批量(增,删,改)操作

package com.xr;import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.math.BigDecimal;
import java.sql.SQLException;
import java.time.LocalDateTime;/*** 测试类* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {//*----------------------批量添加数据-----------------------*/// 创建SqlExecutor对象控制器,并传入连接对象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[批量插入数据]String sql = "insert into user_info(u_name,u_password,u_age,deposit,birth) values(?,?,?,?,?)";// 创建二维数组Object[][] params = {{"李四", "123456", 18, new BigDecimal(1000), LocalDateTime.now()},{"王五", "123456", 18, new BigDecimal(1000), LocalDateTime.now()},{"张三", "123456", 18, new BigDecimal(1000), LocalDateTime.now()}};// 批量增加数据操作int[] i = sqlExecutor.executeBatch(sql,params );//输出结果System.out.println("批量添加数据:" + i.length +"行");/*----------------------批量修改数据-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[批量修改数据]sql = "update user_info set u_password = ? where u_name = ?";// 创建二维数组params = new Object[][]{{"11111","李四"},{"22222","王五"},{"33333","张三"}};// 批量修改数据操作i = sqlExecutor.executeBatch(sql, params);//输出结果System.out.println("批量修改数据:" + i.length +"行");/*----------------------批量删除数据-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[批量删除数据]sql = "delete from user_info where u_name = ?";//定义二位数组params = new Object[][]{{"李四"},{"王五"},{"张三"}};// 批量删除数据操作i = sqlExecutor.executeBatch(sql, params);//输出结果System.out.println("批量删除数据:" + i.length +"行");}
}

3.最后运行结果


通用查询

前面完成了增删改操作,就差一个查询了

当从数据库查询出来的结果后,java代码如何拿到呢???

        1.可以通过Array数组接收

        2.可以通过map集合接收

        3.可以将查询出来的结果映射成java对象

        4.如果只查一列的值,直接返回一个任意基本数据类型接收

这里有4中选项可以选择,将查询出的结果使用多种方式接收他,如果按照常理的方案写的话,需要写多个if,else很麻烦,所以可以择优简便选择使用策略模式


 

策略模式:

策略模式:

        我的理解: 当一个东西可以有很多种选择时,就可以考虑使用策略来简化代码了

        官方解释:

                策略模式是一种设计模式,它允许在运行时选择算法的行为。在策略模式中,可以定义一系列算法,并将每个算法封装在单独的类中。这些算法具有相同的接口,使得它们可以互相替换,而客户端代码则可以根据需要选择合适的算法。

        比如:

                我商城购买了一件东西,付款的时候可有有多种支付方式,如:支付宝支付,微信支付,银联支付,这时也是有多种选择

1.定义一个类型转换接口 ResultSetHandler

作用:

        是封装结果集不同处理方法,将查询的结果转换成不同的形式

ResultSet :结果集,我们通过结果集来转化为其他类型

package com.xr.util;import java.sql.ResultSet;
import java.sql.SQLException;/*** 抽象的结果集处理器* @author xr* @Date 2024/5/8 9:58*/public interface ResultSetHandler<T> {/*** 结果集的处理方法* @param rs 结果集对象* @return 处理后的对象,因为返回的对象是未知类型,定义成泛型* @throws SQLException sql异常*/T handle(ResultSet rs) throws SQLException;
}

【一】将查询结果转换为Array数组

实现思路: 

        1. 定义一个ArrayHandler类,实现ResultSetHandler接口

        2. 定义一个RowProcessor工具类,具体的实现转换方法写在该类

        2. 重写Handle方法,调用RowProcessor工具类中的具体转换实现

为什么不直接到ArrayHandler类中直接写转换的实现,而是又定义一个RowProcessor工具类来实现呢?

        1. 因为要避免将具体的实现逻辑暴露给调用者,因为调用者并不需要知道如何将结果集转换为数组,而是应该只关心如何将结果集转换为数组,所以将具体的实现逻辑写在RowProcessor中,也遵循单一职责原则

        2.看RowProcessor这个类名,他是一个行处理工具类,所以现在将查询出来的结果转换成其他类型都只是转换1条数据,如果多条数据需要转换的话,我们也可以调用行处理器中的实现,简便代码

1.定义ArrayHandler转换类,实现ResultSetHandler接口

作用:

        将查询出来的数据(单条)转换成数组

package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;/*** 数组转换类* @author xr* @Date 2024/5/10 19:31*/public class ArrayHandler implements ResultSetHandler<Object[]> {/*** 将结果集转换为数组* @param rs 结果集* @return 返回转换后的数组* @throws SQLException 抛出sql异常*/@Overridepublic Object[] handle(ResultSet rs) throws SQLException {// 如果结果集有数据,则调用工具类中的方法将结果集转换为数组,否则返回nullreturn rs.next() ? RowProcessor.toArray(rs):null;}
}

2.定义RowProcessor行处理器

作用:

        将转换多个类型具体操作全部写在本类,实施单一职责

在类中定义toArray()方法

      作用: 将查询出的数据结果集转换成数组

package com.xr.utils;import java.sql.ResultSet;
import java.sql.SQLException;/*** 行处理器工具类* @author xr* @Date 2024/5/10 19:34*/public class RowProcessor {/*** 将结果集转换为数组* @param rs 数据集对象* @return 转换后的数组* @throws SQLException 抛出sql异常*/public static Object[] toArray(ResultSet rs) throws SQLException {// 定义数组,使用结果集的元数据中的列数作为数组长度Object[] result = new Object[rs.getMetaData().getColumnCount()];// 遍历结果集for(int i = 0; i< result.length; i++) {// 遍历数组,将结果集的每一列的值赋值给数组,通过结果集的下标获取具体值result[i] = rs.getObject(i+1);}// 返回数组return result;}
}
3. 在SqlExecutor类中定义通用查询方法

executeQuery(String,ResultSetHandler,Object....)

        作用:

                通用返回结果类,并将查询的结果转换成指定的类型

String :sql语句

ResultSetHandler:指定转换类型的实现类

Object...: 参数数组

  /*** 执行查询sql语句,返回指定类型* 实现思路:* 1.将sql语句进行预编译* 2.将sql语句中的占位符对应的参数设置到sql语句中* 3.执行sql语句,返回结果集* 4.调用处理器,将查询的结果转换成指定类型* 5.返回转换之后的类型* @param sql sql语句* @param handler 结果集处理器, 将结果集转换为指定类型* @param params 参数数组* @return 返回转换之后的类型*/public <T> T executeQuery(String sql,ResultSetHandler<T> handler,Object... params) {if (conn == null) {throw new RuntimeException("连接对象为空!!!");}if (sql == null || sql.isEmpty()) {throw new RuntimeException("sql语句为空!!!");}if (handler == null) {throw new RuntimeException("ResultSetHandler为空!!!");}// 定义PreparedStatement对象PreparedStatement ps = null;// 声明结果集对象ResultSet rs = null;// 定义转换后的结果T result = null;try {// 将sql语句进行预编译ps = conn.prepareStatement(sql);// 设置sql语句中占位符对应的参数setPreparedStatementParams(ps,params);//执行sqlrs = ps.executeQuery();// 调用处理器,将查询的结果转换成指定类型result = handler.handle(rs);// 返回转换后的类型return result;}catch (SQLException e) {throw new RuntimeException("预编译sql语句失败!!!",e);} finally {// 关闭ResultSet结果集资源close(rs);// 关闭PreparedStatement对象资源close(ps);// 关闭Connection连接对象资源close();}}


【二】将查询结果转换为Map集合

前面已经实现第一个策略,将查询的结果转换成Array数组

现在将查询结果转换成Map集合,同样也是将这个策略实现ResultSetHandler接口

实现思路:

        1.定义一个类MapHandler,并实现ResultsetHandler接口,重写handle方法

        2.在行转换器RowProcess类中写出转换成map类型方法的具体实现

        3.查询的结果字段作为map集合的key,具体值作为map集合的value

1.定义MapHandler类,实现ResultSetHandler
package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;/*** 将查询的结果ResultSet结果集转换为Map集合* @author xr* @Date 2024/5/11 8:42*/public class MapHandler implements ResultSetHandler<Map<String,Object>> {/*** 将查询的结果ResultSet结果集转换为Map集合* @param rs 结果集* @return 返回转换后的Map集合* @throws SQLException sql异常*/@Overridepublic Map<String, Object> handle(ResultSet rs) throws SQLException {// 如果结果集有数据,则返回Map集合,否则返回nullreturn rs.next() ? RowProcessor.toMap(rs) : null;}
}
2.在RowProcessor类中实现具体转换map集合的方法
    /*** 将查询结果转换成map* 思路:* 1. 获取结果集的元数据* 2. 获取结果集的列数* 3. 遍历结果集,将结果集的每一列的值赋值给map,通过结果集的下标获取具体值* 4. map集合中的key为列名,value为列的值* 5. 返回map* @param rs 数据集对象* @return 返回转换后的map集合* @throws SQLException sql异常*/public static Map<String,Object> toMap(ResultSet rs) throws SQLException {// 定义map数组Map<String,Object> result = new HashMap<>();//循环遍历结果结合for(int i = 0; i<rs.getMetaData().getColumnCount(); i++) {// 获取列名String columnName = rs.getMetaData().getColumnName(i+1);// 获取列值Object object = rs.getObject(i + 1);// 将列名和列值添加到map集合中result.put(columnName,object);}// 返回map数组return result;}

【二.五】测试转换的两个类型(Array和Map)

1.分别创建两个策略
package com.xr;import com.xr.result.ArrayHandler;
import com.xr.result.MapHandler;
import com.xr.utils.Dbutils;
import com.xr.utils.ResultSetHandler;
import com.xr.utils.SqlExecutor;import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;/*** 测试类* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {/*----------------------转换Array数组-----------------------*/// 创建SqlExecutor对象控制器,并传入连接对象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[查询单条数据]String sql = "select * from user_info where u_name = ?";/* 将查询的结果转换成Array数组*///1.创建Array策略ResultSetHandler<Object[]> arrayHandler = new ArrayHandler();//2.调用sqlExecutor工具类的executeQuery方法,传入sql语句和策略和具体参数Object[] objects = sqlExecutor.executeQuery(sql, arrayHandler, "xr");// 查看转换结果Arrays.stream(objects).forEach(System.out::println);System.out.println("-------------------------------转换数组完成------------------------------------");/*----------------------转换Array数组-----------------------*/// 创建SqlExecutor对象控制器,并传入连接对象sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[查询单条数据]sql = "select * from user_info where u_name = ?";/* 将查询的结果转换成map集合*/// 1. 创建Map策略ResultSetHandler<Map<String,Object>> result = new MapHandler();//2.调用sqlExecutor工具类的executeQuery方法,传入sql语句和策略和具体参数Map<String, Object> map = sqlExecutor.executeQuery(sql, result, "xr");// 循环输出结果map.forEach((k,v)->{System.out.println("字段名:"+k+"---->字段值:"+v);});}
}
2.最后转换结果


【三】将结果集转换成Bean对象

将查询的结果转换成bean对象时解决难点:

        1.对象属性名称与数据库字段名称不一样

        2.如何将查询出来的值设置到对象属性中

        3.时间转换问题

解决方案:

        1.添加自定义注解,解决属性名称与数据库字段名称不一致

        2.通过反射的内省机制来设置对象属性值

        3.因为有多种时间类型,mysql版本8之前只有Date时间类型,而mysql8之后多了LocalDateTime时间类型

1.创建一个Cloumn注解【映射数据库名称】

作用:

        将数据库库字段名称映射在实体类中的属性上

package com.xr;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 映射数据库名称* @author xr* @Date 2024/5/11 9:37* @Retention 注解在运行时有效* @Target 注解作用范围,使用范围在属性上*/@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {/*** 数据库字段名*/String name();
}
2. 在实体类中加上自定义Column注解
package com.xr.entity;import com.xr.Column;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;
import java.time.LocalDateTime;/*** userInfo实体* @author xr* @Date 2024/5/8 20:51*/@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {/*** id*/@Column(name="u_id")private Integer id;/*** 用户名*/@Column(name="u_name")private String username;/*** 密码*/@Column(name="u_password")private String password;/*** 年龄*/@Column(name="u_age")private Integer age;/*** 存款* 后面这两个字段与数据库名称一样,所以不需要添加@Column注解*/private BigDecimal deposit;/*** 生日*/private LocalDateTime birth;
}
3.创建一个BeanHandler策略类实现ResultSetHandler接口

为什么要定义class对象?

        1.当我们使用实体类中的属性名与数据库字段名进行匹配时,需要用到反射获取注解和属性

        2.当我们给实体类中的属性设置值时,需要通过反射的内省机制来设置

        3.一切使用到的反射操作,都需要先通过获取Class对象才能使用

package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;/*** 将结果集转换成bean对象* @author xr* @Date 2024/5/11 9:15*/public class BeanHandler<T> implements ResultSetHandler<T> {/*** 定义class对象*/private Class<T> clazz;/*** 构造方法传递类对象* @param clazz 类对象*/public BeanHandler(Class<T> clazz){this.clazz = clazz;}@Overridepublic T handle(ResultSet rs) throws SQLException {// 如果有数据,则返回bean对象,否则返回nullreturn rs.next()? RowProcessor.toBean(rs,clazz) :null;}
}
4.在行RowProcessor行处理器工具类添加实现转换bean对象的方法
/*** 将查询结果转换成Bean对象* 思路:* 1.我要将查询出来的数据转化为对象* 2.数据源rs,拥有数据库所有字段名称,及对应的数据值* 3.clazz,通过clazz对象可以获取里面的所有属性* 4.rs里面的字段名称作为clazz对象的属性* 5.将rs里面的数据值赋值给clazz对象的属性的属性值* 6.由于rs里面的字段名称和clazz对象的属性名称可能不一样* 7.定义了注解@Cloumn进行映射对应的数据库名称* 8.如果属性有@Cloumn注解,则获取对应的属性名称,判断是否和数据库字段一样,一样则赋值* 9.如果属性没有@Cloumn注解,则获取属性名称,因为没有@Cloumn注解,证明属性名称和数据库字段一样* 10.设置值的时候肯能有时间类型转换问题* 11.为什么会有时间问题呢,因为mysql的驱动5和mysql的驱动8,时间类型不一样* 12.设置完成将返回对应的对象* @param rs 结果集* @param clazz 对象对应的类* @return 返回转换后的bean对象* @throws SQLException sql异常*/public static <T> T toBean(ResultSet rs,Class<T> clazz) throws SQLException {// 获取类中的所有属性,通过内省机制获取PropertyDescriptor[] pds = getPropertyDescriptors(clazz);// 获取对象实例,由于getConstructor,newInstance需要抛出异常,// 则将他们提取出来,定义另一个方法抛出异常T bean = newInstance(clazz);for (int i = 0 ; i < rs.getMetaData().getColumnCount(); i++) {/* 获取列名,通过类名判断是否与属性名一样 */String columnLabel = rs.getMetaData().getColumnLabel(i+1);// 循环遍历属性for (PropertyDescriptor pd : pds) {// 判断属性名称和数据库字段是否一样if(checkColumn(columnLabel,pd,clazz)) {// 属性名称和数据库字段一样,则设置对应的值/** columnLabel:数据库字段名称* rs:结果集,拥有所有数据库字段名称及对应的数据值* pd:属性描述器,属性名称* bean:对象实例* */setBean(columnLabel,rs,pd,bean);break;}}}return bean;}

1.toBean(Result,Class)方法,rs结果集对象,Class为类对象;

2.这个方法里面又调用了其他4个方法

        1.getPropertyDescriptors(Class<?> class)         获取所有属性

        2.newInstance(Class);        获取对象实例

        3.checkColumn(Stirng,PropertyDescriptor, Class<?> )   判断数据库字段名称是否与属性名称一致

        4.setBean(String,ResultSet,PropertyDescriptor,T)          设置对象的属性值

3.为什么要定义这4方法呢????

        1. 因为这些方法都需要抛出异常,如果全部写在toBean方法内,使得代码的阅读性糟糕

getPropertyDescriptors(Class<?>);

参数:

        Class<?> : 实体类的Class对象

作用:

        获取实体类中所有属性;

在RowProcessor行处理器类中添加该方法

    /*** 获取类中的所有属性【内省机制获取】* @param clazz 类* @return 返回属性数组*/private static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) {try {// 创建BeanInfo对象BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);// 获取所有属性,返回属性数组return beanInfo.getPropertyDescriptors();} catch (IntrospectionException e) {throw new RuntimeException("获取属性描述器失败",e);}}

newInstance(Class<T> clazz);

参数:

        Class:实体类的Class对象

作用:

        获取实体类对象的实例

在RowProcessor行处理器类中添加该方法

    /*** 获取对象实例* @param clazz 类的class对象* @return 返回对象实例*/private static <T> T newInstance(Class<T> clazz) {try {return clazz.getConstructor().newInstance();} catch (Exception e) {throw new RuntimeException("创建对象失败!!!",e);}}

checkColumn(String,PropertyDescriptor, Class<?>);

参数:

                    String :         数据库字段名称

PropertyDescriptor:       属性

               Class<?>:         实体类的Class对象

作用 :

        判断属性名称和数据库字段是否一样

在RowProcessor行处理器类中添加该方法

    /*** 判断是否与属性名一样*/private static boolean checkColumn(String columnName, PropertyDescriptor pd, Class<?> clazz) {// 获取属性名称String beanName = pd.getName();try {// 通过属性名称获取对象的属性Field fieldName = clazz.getDeclaredField(beanName);// 判断属性是否有注解// 如果有注解,代表属性名称与数据库字段名称不一样,需要进行对比// 如果没有,则代表属性名称与数据库名称一样if (fieldName.isAnnotationPresent(Column.class)) {// 获取属性上的注解值beanName = fieldName.getAnnotation(Column.class).name();}return beanName.equalsIgnoreCase(columnName);} catch (NoSuchFieldException e) {throw new RuntimeException("判断属性名称失败!",e);}}
setBean(String,ResultSet,PropertyDescriptor, T);

参数:

                    String :         数据库字段名称

               ResultSet:         结果集

PropertyDescriptor:       属性

                           T :       对象

作用:

        设置实体类属性对应的值

在RowProcessor行处理器类中添加该方法

  /**** @param columnLabel 列名* @param rs 数据集* @param pd 属性* @param instance 实例对象* @param <T> 返回转换后的对象*/private static <T> void setBean(String columnLabel,ResultSet rs,PropertyDescriptor pd, T instance) {try {// 通过名称获取数据库查询的具体值Object value = rs.getObject(columnLabel);// 如果是基本类型,并且获取的值为空,直接返回if(pd.getPropertyType().isPrimitive() && value == null) {return;}// 通过传递过来来的字段获取set方法,并调用通过invoke方法调用set方法pd.getWriteMethod().invoke(instance,value);} catch (SQLException | IllegalAccessException | InvocationTargetException e) {throw new RuntimeException("获取具体值失败,设置具体值失败!!!",e);}}
5.最后在测试类中进行测试结果
package com.xr;import com.xr.entity.UserInfo;
import com.xr.result.BeanHandler;
import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.sql.SQLException;/*** 测试类* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {/*----------------------转换Bean对象-----------------------*/// 创建SqlExecutor对象控制器,并传入连接对象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定义sql语句,[查询单条数据]String sql = "select * from user_info where u_name = ?";// 创建Bean策略BeanHandler<UserInfo> userInfoBeanHandler = new BeanHandler<>(UserInfo.class);//调用sqlExecutor工具类的executeQuery方法,传入sql语句和策略和具体参数UserInfo xr = sqlExecutor.executeQuery(sql, userInfoBeanHandler, "xr");// 输出结果System.out.println(xr);System.out.println("-------------------------------转换bean对象完成------------------------------------");}
}
6. 最后执行结果


------------------------------------------------------------待更新------------------------------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/10777.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索数据结构:树与二叉树

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 树 1.1. 树的定义 树是一种非线性的数据结构&#xff0c;它是由n&a…

ORA-609频繁出现在alert.log,如何解决?

ORA-609就alertlog中比较常见的一个报错&#xff0c;虽然并没有太大的影响&#xff0c;但是频繁的出现在alert log也是很让人厌烦的事情&#xff0c;本文介绍如何排查解决ORA-609问题。 1.ORA-609官方定义 could not attach to incoming connection Cause Oracle process cou…

【SRC实战】前端脱敏信息泄露

挖个洞先 https://mp.weixin.qq.com/s/xnCQQCAneT21vYH8Q3OCpw “ 以下漏洞均为实验靶场&#xff0c;如有雷同&#xff0c;纯属巧合 ” 01 — 漏洞证明 一、前端脱敏&#xff0c;请求包泄露明文 “ 前端脱敏处理&#xff0c;请求包是否存在泄露&#xff1f; ” 1、获取验…

|Python新手小白中级教程|第二十八章:面向对象编程(类定义语法私有属性类的继承与多态)(4)

文章目录 前言一、类定义语法二、私有方法和私有属性1.私有属性2.私有方法 三、类“继承”1.初识继承2.使用super函数调用父类中构造的东西 四、类“多态”1.多态基础2.子类不同形态3.使用isinstance函数与多态结合判断类型 总结 前言 大家好&#xff0c;我是BoBo仔吖&#xf…

6818Linux内核开发移植

Linux内核开发移植 Linux内核版本变迁及其获得 Linux是最受欢迎的自由电脑操作系统内核&#xff0c; 是一个用C语言写成&#xff0c; 并且符合POSIX标准的类Unix操作系统 Linux是由芬兰黑客Linus Torvalds开发的&#xff0c; 目的是尝试在英特尔x86架构上提供自由免费的类Un…

Task Office for Mac v9.0激活版:任务管理新境界

还在为繁琐的任务管理而烦恼吗&#xff1f;Task Office for Mac为您带来全新的任务管理体验。简洁明了的界面设计&#xff0c;让您轻松上手&#xff1b;强大的任务管理和项目管理功能&#xff0c;让您轻松掌握任务进度&#xff1b;多用户协作功能&#xff0c;让团队协作更加高效…

Excel办公技巧之下拉菜单

在日常办工中&#xff0c;经常需在单元格中输入特定的值&#xff0c;此时我们可以使用下拉菜单解决&#xff0c;输入错误和错误值&#xff0c;可以一劳永逸的解决固定数据输入问题。 使用Excel下拉菜单时&#xff0c;它在数据输入和验证方面发挥着重要作用通过点击单元格的下拉…

商业数据分析--时间序列图及趋势分析

绘制时间序列图,并指出存在什么样的状态如上两图: 可见状态:从时间序列图可以看出,这些数据存在明显的季节性波动,每年的第4季度值都最高,而第2季度值最低。同时也存在一些下降的趋势。 通过引进虚拟变量,建立多元线性回归模型。答: 通过引入虚拟变量,我们可以建立如下的…

商场学习之微服务

前言 寒假前在新电脑上配置了java环境&#xff0c;maven仓库&#xff0c;node,js&#xff0c;navicat&#xff0c;MySQL&#xff0c;linux&#xff0c;vmware等环境&#xff0c;创建了6个mysql数据库&#xff0c;77张表。 如此多的表&#xff0c;字段&#xff0c;去手写基础…

Web入门——三栏布局页面

前置知识 内外边距 内边距(padding)&#xff1a; padding是元素边框与其内容之间的空间。也就是说&#xff0c;如果你给一个元素设置了内边距&#xff0c;这个空间会作为元素内容与元素边框之间的缓冲区域。设置内边距会使元素本身变大。例如padding:10px就创建了10像素的空间…

Qt之QMqtt 发送图片数据

简述 MQTT(消息队列遥测传输)是ISO标准下基于发布/订阅范式的消息协议;它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件; MQTT是一个基于客户端-服务器的消息发布/订阅传输协议;MQT…

Docker in Docker(DinD)原理与实战

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Docker幻想曲&#xff1a;从零开始&#xff0c;征服容器宇宙》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Docker简介 2、Docker …

使用 AI Assistant for Observability 和组织的运行手册增强 SRE 故障排除

作者&#xff1a;Almudena Sanz Oliv, Katrin Freihofner, Tom Grabowski 通过本指南&#xff0c;你的 SRE 团队可以实现增强的警报修复和事件管理。 可观测性 AI 助手可帮助用户使用自然语言界面探索和分析可观测性数据&#xff0c;利用自动函数调用来请求、分析和可视化数据…

windows系统安装Ubuntu子系统

安装前先在 控制面板 中打开 程序与功能选项 &#xff0c;点击 启用或关闭Windows功能&#xff1a; 勾选 适用于 Linux的Windows子系统 和 虚拟机平台 、 Hyper-v 。 重启电脑后再 Microsoft Store Windows应用商店 中下载合适的Ubuntu版本。 运行Ubuntu程序&#xff0c;如出现…

【实战】算法思路总结

面试过程中&#xff0c;总是被拷打&#xff0c;信心都要没了。但是也慢慢摸索出一些思路&#xff0c;希望对大家有帮助。 &#xff08;需要多用一下ACM模式&#xff0c;力扣模式提供好了模板&#xff0c;自己在IDEA里面写的话&#xff0c;还是会有些陌生&#xff09; 0、基本…

Unity Editor 找物体助手

找啊找朋友~ &#x1f371;功能介绍&#x1f959;使用方法 &#x1f371;功能介绍 &#x1f4a1;输入相关字符串&#xff0c;它会帮你找到名称中带有该字符串的所有物体&#xff0c;还会找包含该字符串的Text、TextMeshProUGUI。 &#x1f959;使用方法 &#x1f4a1;导入插…

小学拼音弄一下

import re from xpinyin import Pinyindef remove_middle_characters(text):# 仅保留汉字chinese_chars re.findall(r[\u4e00-\u9fff], text)cleaned_text .join(chinese_chars)# 如果字符数为偶数&#xff0c;则在中间添加空格if len(cleaned_text) % 2 0:middle_index le…

【北京迅为】《iTOP-3588从零搭建ubuntu环境手册》-第5章 安装SSH

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

从0开始学python(七)

目录 前言 1 break、continue和pass函数 1.1 break 1.2 continue 1.3 pass 2、序列的索引及切片操作 2.1字符串的索引和切片 2.1.1 字符串索引 2.1.2 字符串切片 总结 前言 上一篇文章我们介绍了python中的循环结构&#xff0c;包括for和while的使用。本章接着往下讲。…

腾讯云服务器之ssh远程连接登录及转发映射端口实现内网穿透(实现服务器访问本地电脑端口)

目录 一、创建密钥绑定实例二、设置私钥权限三、ssh远程连接到服务器四、修改root密码五、端口转发&#xff08;实现服务器访问本地电脑的端口&#xff09; 一、创建密钥绑定实例 创建密钥会自动下载一个私钥&#xff0c;把这个私钥复制到c盘 二、设置私钥权限 1、删除所有用户…