欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

对象映射框架ORM(jdbc中CRUD的简易封装)

程序员文章站 2024-01-11 16:46:46
简易的对象映射框架在使用jdbc的时候,往往都需要对数据库进行一些列的增删改查操作,但是每次使用jdbc的时候,难免不能避免使用jdbc的六大步骤:1,加载驱动2,获取链接3,根据连接获取发送并执行SQL语句的statement对象4,执行SQL语句5,处理结果6,关闭资源因此,我们可以将CRUD中的公共部分拿出来,做一个简易的封装public class DBUtils {/** 驱动类路径 */private static final String DRIVER_C...

简易的对象映射框架

  • 在使用jdbc的时候,往往都需要对数据库进行一些列的增删改查操作,但是每次使用jdbc的时候,难免不能避免使用jdbc的六大步骤:
    • 1,加载驱动
    • 2,获取链接
    • 3,根据连接获取发送并执行SQL语句的statement对象
    • 4,执行SQL语句
    • 5,处理结果
    • 6,关闭资源
  • 因此,我们可以将CRUD中的公共部分拿出来,做一个简易的封装
public class DBUtils { /** 驱动类路径 */ private static final String DRIVER_CLASS="com.mysql.jdbc.Driver"; /** url地址 */ private static final String URL="jdbc:mysql://127.0.0.1:3306/mydb2"; /** 数据库服务器登录用户名 */ private static final String USER="root"; /** 数据库服务器登录密码 */ private static String PASSWORD= "123456"; static { try { //1.加载驱动 Class.forName(DRIVER_CLASS); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /**
	 * 2. 获取数据库连接对象
	 * 
	 * @return
	 */ public static synchronized Connection getConn() { try { return DriverManager.getConnection(URL, USER, PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return null; } /**
	 * 6.关闭资源
	 * 
	 * @param rs
	 * @param stat
	 * @param conn
	 */ public static void close(ResultSet rs, Statement stat, Connection conn) { try { if (rs != null) rs.close(); if (stat != null) stat.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } 
  • 因为无论是执行 添加,修改,删除,还是查询,都会涉及到:加载驱动,获取链接以及关闭资源,所有我们将这三个步骤进行一个简易的封装,而且,在添加,修改,删除这几个步骤中,仅仅就是SQL语句的差别,所以,我们也可以进行简易的封装:
/**
	 * 封装通用的更新操作:INSERT UPDATE DELETE
	 * 
	 * @param sql
	 * @param params 等价于:Object[] params 
	 * @return
	 */ public static boolean exeUpdate(String sql, Object... params) { // 获取连接 Connection conn = getConn(); PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } } return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); } finally { close(null, ps, conn); } return false; } 
  • 这个是对insert,delete,update的封装,在执行这些操作的时候,我们只需要传入一个SQL语句,以SQL语句中需要的参数就可以进行增,删,改操作,并返回一个Boolean类型的结果,相比于没有封装前,代码量减少了可不是一点点。
  • 然后就是DQL操作的封装了,查询的操作封装相对于前面的DML来说要复杂一些,因为我们在进行查询操作之后,会返回一个结果集,这个时候,我们就需要处理这个结果集。
    • 1,封装查询一条数据,并返回一个java对象(前提是必须要有一个javabean)
      在封装查询语句之前,将执行SQL语句之后获得的结果集为一个map集合,我们先通过反射定义一个方法将这个map集合转化为一个java对象:
** * 将Map集合转换为一个确定的类型 * * @param <T> * @param map * @param t * @return */ private static <T> T mapToBean(Map<String, Object> map, Class<T> t) { //		T obj = null; try { // 根据提供的Class对象创建对应类型的Object T obj = t.newInstance(); // 获取Class中的所有Field //			Field[] fields = t.getDeclaredFields(); //			//遍历获取每一个属性对象 //			for (Field field : fields) { //				//获取属性名 //				String fname = field.getName(); //				//获取属性值 //				Object value = map.get(fname); //				if(Objects.nonNull(value)) { //					//设置属性对象的可访问性 //					field.setAccessible(true); //					//将从map中获取的值设置给属性  obj.name=XX //					field.set(obj, value); //				} //			} map.forEach((k, v) -> { try { // 根据Field名称获取字段对象 Field field = t.getDeclaredField(k); // 设置字段的可访问性 field.setAccessible(true); // 为字段设置值 field.set(obj, v); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); return obj; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // 返回对象 return null; } 
  • 然后进行查询一条语句的封装方法:
/**
     * 封装通用查询单条数据的方法
     *
     * JDBC,反射,集合框架,lambda表达式,新增Objects类
     *
     * @param <T>
     * @param t
     * @param sql
     * @param params
     * @return
     */ public static <T> T queryOne(Class<T> t, String sql, Object... params) { // 获取查询到到数据集合 List<Map<String, Object>> list = queryMaps(sql, params); if (list.size() > 0) { // 获取一个Map对象 Map<String, Object> map = list.get(0); // 将map集合转换为Javabean并返回 return mapToBean(map, t); } return null; } 
  • 2,封装查询多条语句的查询语句,并返回一个Javabean的List集合,在这之前,我们先要将查询语句返回的结果集变为一个map集合的List集合,听起来有点绕口,通俗的来讲就是:将查询语句返回的多条数据用一个List集合装起来,而这个list集合中的元素是多个Map集合:
 /**
     * 执行相关查询并将结果返回为List<Map<String,Object>> 集合
     *
     * @param sql
     * @param params
     * @return
     */ public static List<Map<String, Object>> queryMaps(String sql, Object... params) { // 声明动态数组用于存储每一个查询到的Map对象 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); // 获取连接 Connection conn = getConn(); PreparedStatement ps = null; ResultSet rs = null; try { // 获取预处理sql命令的对象 ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { // 对指定位置的占位符填充值(预处理) ps.setObject(i + 1, params[i]); } } // 执行查询获取结果集 rs = ps.executeQuery(); // 获取结果集的元数据对象ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 获取总查询列数 int columnCount = rsmd.getColumnCount(); // 遍历结果集 while (rs.next()) { // 声明map集合存储每一条数据(临时缓存) Map<String, Object> map = new HashMap<String, Object>(); // 遍历获取每一列的信息 for (int i = 1; i <= columnCount; i++) { // 获取列名称(作为map集合的键) String key = rsmd.getColumnName(i); // 获取列标签 String label = rsmd.getColumnLabel(i); // 获取列值(作为map集合的值) Object value = rs.getObject(label); if (Objects.nonNull(value)) { // 将取到的每一列的列名与列值存储到map map.put(key, value); } } // 将map集合装入list list.add(map); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, ps, conn); } return list; } 
  • 然后在封装成为查询多条数据的方法:
  • 大致思路就是:先查询多条数据,将它用一个List集合装起来,list集合中的元素是Map集合,然后在遍历,将每一个map集合通过mapToBean方法转化为一个Javabean,最后放回的结果就是一个List集合,集合中的元素就是一个个Javabean。
 /**
     * 封装通用查询多条数据的方法
     *
     * @param <T>
     * @param t
     * @param sql
     * @param params
     * @return
     */ public static <T> List<T> queryList(Class<T> t, String sql, Object... params) { List<T> list = new ArrayList<T>(); // 获取所有查询的到的数据 List<Map<String, Object>> maps = queryMaps(sql, params); // 遍历集合中每一条数据(map) maps.forEach(m -> { // 将map转换为Javabean T obj = mapToBean(m, t); // 将Javabean装入list list.add(obj); }); return list; } 
  • 3,多表联合查询方法的封装,因为多表联合查询的结果对应的Javabean在查询之前是不确定的,所以我们查询之后也就只能返回一个List集合,集合中的元素是一个个map集合,如果需要将其转换为一个Javabean,那么就需要自行先创建一个对应的Java类。
/**
     * 执行相关查询并将结果返回为List<Map<String,Object>> 集合
     *
     * @param sql
     * @param params
     * @return
     */ public static List<Map<String, Object>> queryMaps(String sql, Object... params) { // 声明动态数组用于存储每一个查询到的Map对象 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); // 获取连接 Connection conn = getConn(); PreparedStatement ps = null; ResultSet rs = null; try { // 获取预处理sql命令的对象 ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { // 对指定位置的占位符填充值(预处理) ps.setObject(i + 1, params[i]); } } // 执行查询获取结果集 rs = ps.executeQuery(); // 获取结果集的元数据对象ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 获取总查询列数 int columnCount = rsmd.getColumnCount(); // 遍历结果集 while (rs.next()) { // 声明map集合存储每一条数据(临时缓存) Map<String, Object> map = new HashMap<String, Object>(); // 遍历获取每一列的信息 for (int i = 1; i <= columnCount; i++) { // 获取列名称(作为map集合的键) String key = rsmd.getColumnName(i); // 获取列标签 String label = rsmd.getColumnLabel(i); // 获取列值(作为map集合的值) Object value = rs.getObject(label); if (Objects.nonNull(value)) { // 将取到的每一列的列名与列值存储到map map.put(key, value); } } // 将map集合装入list list.add(map); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, ps, conn); } return list; } 

以上就是对jdbc中CRUD的简易封装了,不过还没有涉及到数据库连接池以及jdbc事务的内容,所以不需要借助第三方的jar包的导入以及配置文件的书写,但是对于一些平常的jdbc需求已经是可以满足了,那么下面我将完整的ORM封装书写下来:

package com.softeem.DButils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; import com.alibaba.druid.pool.DruidDataSource; /**
 * 封装增删改查操作:最终版
 * @author 黄小宝
 */ public class DButils { /** 驱动类路径 */ private static String DRIVER_CLASS; /** url地址 */ private static String URL; /** 数据库服务器登录用户名 */ private static String USER; /** 数据库服务器登录密码 */ private static String PASSWORD; /**最大活动链接数*/ private static int MAX_ACTIVE; /**最长等待连接获取的时间*/ private static long MAX_WAIT; /**初始链接数*/ private static int INIT_SIZE; /**声明连接池引用*/ private static DruidDataSource ds; static { try { //读取属性文件获取连接数据库相关的字符串 //InputStream is = DButils.class.getResourceAsStream("src/jdbc.properties"); InputStream is = new FileInputStream(new File("C:\\Users\\黄小宝\\Desktop\\jdbc.txt")); //创建属性对象 Properties p = new Properties(); //加载包含属性信息的输入流 System.out.println(is); p.load(is); //根据属性名获取属性值 DRIVER_CLASS = p.getProperty("driver"); URL = p.getProperty("url"); USER = p.getProperty("user"); PASSWORD = p.getProperty("password"); //获取连接池相关配置 MAX_ACTIVE = Integer.parseInt(p.getProperty("pool.maxActive")); INIT_SIZE = Integer.parseInt(p.getProperty("pool.initSize")); MAX_WAIT = Long.parseLong(p.getProperty("pool.maxWait")); //初始化连接池 init(); } catch (IOException e) { e.printStackTrace(); } } /**
     * 初始化连接池
     */ public static void init() { ds = new DruidDataSource(); ds.setDriverClassName(DRIVER_CLASS); ds.setUrl(URL); ds.setUsername(USER); ds.setPassword(PASSWORD); ds.setMaxActive(MAX_ACTIVE); ds.setInitialSize(INIT_SIZE); ds.setMaxWait(MAX_WAIT); } /**
     * 2. 获取数据库连接对象
     * @return
     */ public static synchronized Connection getConn() { try { //当连接池被关闭或者为null重新初始化 if(ds == null || ds.isClosed()) { init(); } //从连接池中获取一个连接并返回 return ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } /**
     * 6.关闭资源
     *
     * @param rs
     * @param stat
     * @param conn
     */ public static void close(ResultSet rs, Statement stat, Connection conn) { try { if (rs != null) rs.close(); if (stat != null) stat.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } /**
     * 封装通用的更新操作:INSERT UPDATE DELETE
     *  使用同一个连接对象,避免出现事务的原子性问题(在传递该连接对象时,可以通过构造器进行传参)
     * @param sql
     * @param params Object[] params
     * @return
     */ public static boolean exeUpdate(Connection conn,String sql, Object... params) { PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } } return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); } finally { close(null, ps, null); } return false; } /**
     * 封装通用的更新操作:INSERT UPDATE DELETE
     * 不需要使用到同一个连接对象时,调用该方法 :
     * 每调用一次:getConn()方法,就会创建一个新的connection对象,而事务中必须要满足原子性,即同一个连接对象,
     * 所以该方法不满足事务的要求
     * @param sql
     * @param params Object[] params
     * @return
     */ public static boolean exeUpdate(String sql, Object... params) { Connection conn = getConn(); PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } } return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); } finally { close(null, ps, conn); } return false; } /**
     * 封装通用查询单条数据的方法
     *
     * JDBC,反射,集合框架,lambda表达式,新增Objects类
     *
     * @param <T>
     * @param t
     * @param sql
     * @param params
     * @return
     */ public static <T> T queryOne(Class<T> t, String sql, Object... params) { // 获取查询到到数据集合 List<Map<String, Object>> list = queryMaps(sql, params); if (list.size() > 0) { // 获取一个Map对象 Map<String, Object> map = list.get(0); // 将map集合转换为Javabean并返回 return mapToBean(map, t); } return null; } /**
     * 封装通用查询多条数据的方法
     *
     * @param <T>
     * @param t
     * @param sql
     * @param params
     * @return
     */ public static <T> List<T> queryList(Class<T> t, String sql, Object... params) { List<T> list = new ArrayList<T>(); // 获取所有查询的到的数据 List<Map<String, Object>> maps = queryMaps(sql, params); // 遍历集合中每一条数据(map) maps.forEach(m -> { // 将map转换为Javabean T obj = mapToBean(m, t); // 将Javabean装入list list.add(obj); }); return list; } /**
     * 将Map集合转换为一个确定的类型
     *
     * @param <T>
     * @param map
     * @param t
     * @return
     */ private static <T> T mapToBean(Map<String, Object> map, Class<T> t) { //		T obj = null; try { // 根据提供的Class对象创建对应类型的Object T obj = t.newInstance(); // 获取Class中的所有Field //			Field[] fields = t.getDeclaredFields(); //			//遍历获取每一个属性对象 //			for (Field field : fields) { //				//获取属性名 //				String fname = field.getName(); //				//获取属性值 //				Object value = map.get(fname); //				if(Objects.nonNull(value)) { //					//设置属性对象的可访问性 //					field.setAccessible(true); //					//将从map中获取的值设置给属性  obj.name=XX //					field.set(obj, value); //				} //			} map.forEach((k, v) -> { try { // 根据Field名称获取字段对象 Field field = t.getDeclaredField(k); // 设置字段的可访问性 field.setAccessible(true); // 为字段设置值 field.set(obj, v); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); return obj; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // 返回对象 return null; } /**
     * 执行相关查询并将结果返回为List<Map<String,Object>> 集合
     *
     * @param sql
     * @param params
     * @return
     */ public static List<Map<String, Object>> queryMaps(String sql, Object... params) { // 声明动态数组用于存储每一个查询到的Map对象 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); // 获取连接 Connection conn = getConn(); PreparedStatement ps = null; ResultSet rs = null; try { // 获取预处理sql命令的对象 ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { // 对指定位置的占位符填充值(预处理) ps.setObject(i + 1, params[i]); } } // 执行查询获取结果集 rs = ps.executeQuery(); // 获取结果集的元数据对象ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 获取总查询列数 int columnCount = rsmd.getColumnCount(); // 遍历结果集 while (rs.next()) { // 声明map集合存储每一条数据(临时缓存) Map<String, Object> map = new HashMap<String, Object>(); // 遍历获取每一列的信息 for (int i = 1; i <= columnCount; i++) { // 获取列名称(作为map集合的键) String key = rsmd.getColumnName(i); // 获取列标签 String label = rsmd.getColumnLabel(i); // 获取列值(作为map集合的值) Object value = rs.getObject(label); if (Objects.nonNull(value)) { // 将取到的每一列的列名与列值存储到map map.put(key, value); } } // 将map集合装入list list.add(map); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, ps, conn); } return list; } /**
     * 根据提供的查询语句以及查询参数,返回符合条件的数目
     *
     * @param sql
     * @param params
     * @return
     */ public static int queryCount(String sql, Object... params) { Connection conn = getConn(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } } rs = ps.executeQuery(); if(rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); }finally { close(rs, ps, conn); } return 0; } } 
  • 在使用的时候也是非常简单的:例如我给大家做一下简单的测试:
package com.softeem.day804.Utils; import java.util.List; import com.softeem.day804.VO.Emp; public class Test { public static void main(String[] args) { String sql = "select * from emp"; List<Emp> list = DBUtils.queryList(Emp.class, sql); list.forEach(l->System.out.println(l)); } } 
  • 执行结果(返回的都是一个个Java对象):
Emp [eno=2, ename=阿卡丽, job=经理, hiredate=2017-04-01 00:00:00.0, age=31, sex=, sal=9800.0, dno=0] Emp [eno=3, ename=露露, job=普通员工, hiredate=2020-03-22 00:00:00.0, age=22, sex=, sal=2200.0, dno=40] Emp [eno=5, ename=阿木木, job=普通员工, hiredate=2017-11-22 00:00:00.0, age=27, sex=, sal=5000.0, dno=40] Emp [eno=6, ename=阿德, job=普通员工, hiredate=2018-08-05 00:00:00.0, age=21, sex=, sal=3160.0, dno=30] Emp [eno=7, ename=艾希, job=经理, hiredate=2015-03-03 00:00:00.0, age=29, sex=, sal=10500.0, dno=0] Emp [eno=8, ename=易大师, job=经理, hiredate=2016-09-10 00:00:00.0, age=35, sex=, sal=8900.0, dno=30] Emp [eno=9, ename=甄姬, job=普通员工, hiredate=2017-09-09 00:00:00.0, age=27, sex=, sal=9900.0, dno=0] Emp [eno=10, ename=后裔, job=普通员工, hiredate=2020-01-01 00:00:00.0, age=30, sex=, sal=8400.0, dno=0] Emp [eno=11, ename=猪八戒, job=普通员工, hiredate=2020-07-22 00:00:00.0, age=45, sex=, sal=3200.0, dno=0] Emp [eno=12, ename=廉颇, job=普通员工, hiredate=2018-07-06 00:00:00.0, age=55, sex=, sal=6800.0, dno=40] Emp [eno=13, ename=李青, job=普通员工, hiredate=2019-07-22 00:00:00.0, age=36, sex=, sal=6900.0, dno=50] Emp [eno=14, ename=李白, job=经理, hiredate=2016-01-01 00:00:00.0, age=36, sex=, sal=16600.0, dno=40] Emp [eno=15, ename=赵信, job=经理, hiredate=2018-09-11 00:00:00.0, age=38, sex=, sal=7500.0, dno=50] Emp [eno=16, ename=安妮, job=普通员工, hiredate=2019-10-11 00:00:00.0, age=18, sex=, sal=3900.0, dno=60] Emp [eno=17, ename=提莫, job=普通员工, hiredate=2019-10-12 00:00:00.0, age=19, sex=, sal=5600.0, dno=60] Emp [eno=18, ename=韦鲁斯, job=经理, hiredate=2018-05-10 00:00:00.0, age=30, sex=, sal=7800.0, dno=60] Emp [eno=19, ename=德莱文, job=普通员工, hiredate=2018-09-11 00:00:00.0, age=35, sex=, sal=9000.0, dno=50] Emp [eno=20, ename=艾维利亚, job=普通员工, hiredate=2017-06-30 00:00:00.0, age=26, sex=, sal=2200.0, dno=30] Emp [eno=123, ename=黄国志, job=null, hiredate=null, age=0, sex=null, sal=0.0, dno=0] 
  • 以上就整个CRUD的简易封装了,如有错误还请指出

本文地址:https://blog.csdn.net/weixin_44846862/article/details/107880286