乘风原创程序

  • 对象映射框架ORM(jdbc中CRUD的简易封装)
  • 2020/8/10 9:54:50
  • 简易的对象映射框架

    • 在使用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