一、ORM介绍
ORM(Object-Relationl Mapping)对象关系映射,一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。它的作用是在关系型数据库和对象之间作一个映射.。1. 技术栈: (1)java+mysql+反射+自定义注解+泛型+jdbc.
2. 持久层框架: 与数据库交互的一层称为持久层(dao)。完成orm操作。
3. o:(Object对象) r:(relative关系) m:(mapping映射)。 实体类---数据库表 属性--表的字段 实体类对象--一条记录 集合---表中多条记录。
4. 手撕持久层框架: 自己编写持久层框架 可以完成无需写sql语句即可完成对单表的CRUD操作。
二、建一个maven的java工程
在idea里面创建一个maven工程
工程结构
引入依赖
<dependencies> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> </dependencies>
创建数据源的属性文件
#配置数据源信息 driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username=root password=root
创建DbUtil工具类
package com.zql.util; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; public class DbUtil { //数据源对象 private static DataSource dataSource; static { try { //创建一个属性对象 Properties properties = new Properties(); //加载属性文件 InputStream inputStream = DbUtil.class.getClassLoader().getResourceAsStream("db.properties"); properties.load(inputStream); //获取连接池对象 dataSource= DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } /** * 获取数据库连接。 * * 本方法通过调用dataSource对象的getConnection方法来获取一个数据库连接。 * 使用者不需要直接与数据库连接细节交互,而是通过调用此方法来获取连接, * 降低了代码与特定数据库实现的耦合度,提高了代码的可维护性和可扩展性。 * * @return Connection 返回一个数据库连接对象。 * @throws Exception 如果获取连接过程中出现错误,将抛出异常。 */ public static Connection getConnection() throws Exception{ Connection coon= dataSource.getConnection(); return coon; } /** * 关闭数据库连接及相关资源。 * * 该方法用于关闭 JDBC 中的 Connection、PreparedStatement 和 ResultSet 对象。 * 它们在使用后应该被关闭,以释放与数据库的连接和资源。 * 方法通过检查每个对象是否为 null,避免了空指针异常。 * 如果关闭资源时发生 SQLException,方法会将其转换为 RuntimeException 抛出。 * 这种做法是为了确保资源始终被关闭,同时将数据库层面的异常转换为运行时异常, * 以便调用者可以更方便地处理或记录异常。 * * @param conn 需要关闭的数据库连接对象。 * @param ps 需要关闭的 PreparedStatement 对象。 * @param rs 需要关闭的 ResultSet 对象。 */ //释放资源 public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) { // 优先关闭 ResultSet,因为它可能依赖于 PreparedStatement 的状态。 if(rs!=null){ try { rs.close(); } catch (SQLException e) { throw new RuntimeException(e); } } // 接着关闭 PreparedStatement。 if(ps!=null){ try { ps.close(); } catch (SQLException e) { throw new RuntimeException(e); } } // 最后关闭 Connection。 if(conn!=null){ try { conn.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } }
通用的添加/修改/删除
package com.zql.dao; import com.zql.annotation.TableField; import com.zql.annotation.TableId; import com.zql.annotation.TableName; import com.zql.util.DbUtil; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.List; public class BaseDao<T>{ /** * 在数据库中插入一个T类型的实体对象。 * <p> * 此方法首先构造SQL插入语句,然后通过反射获取实体类的属性信息, * 包括表名、列名和对应的属性值。如果实体类的字段上有特定注解, * 则会根据注解调整列名。最后,执行SQL语句并返回影响行数。 * * @param t 待插入的实体对象,必须是T类型的实例,其中T代表一个映射到数据库表的类。 * 对象的属性将被用来构建SQL语句的列名和值。 * @return 返回执行SQL语句后影响的行数,成功插入时通常为1。 * @throws Exception 当数据库连接、预编译SQL语句或执行SQL语句时可能出现异常。 */ public int insert(T t) throws Exception { // 1. 构建SQL字符串,初始为"insert into" StringBuffer sql = new StringBuffer("insert into "); // 2. 获取实体类的反射类,用于后续获取表名和属性信息 Class<?> aClass = t.getClass(); // 3. 获取表名,优先考虑@TableName注解提供的值 String tableName = aClass.getSimpleName(); // 默认使用类名作为表名 TableName annotation = aClass.getAnnotation(TableName.class); if (annotation != null) { tableName = annotation.value(); } sql.append(tableName); // 4-5. 遍历实体类的所有属性,收集列名和对应的值 List<String> columnNames = new ArrayList<>(); List<Object> values = new ArrayList<>(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields) { String name = field.getName(); // 获取属性名 // 跳过@TableId注解的字段 TableId tableIdAnnotation = field.getAnnotation(TableId.class); if (tableIdAnnotation != null) { continue; } // 根据@TableField注解调整列名 TableField fieldAnnotation = field.getAnnotation(TableField.class); if (fieldAnnotation != null) { name = fieldAnnotation.value(); } field.setAccessible(true); Object value = field.get(t); // 获取属性值 values.add("'"+value+"'"); // 将值包装为字符串形式 columnNames.add(name); } // 6. 将收集的列名和值转换为SQL语法格式 String columns = columnNames.toString().replace("[", "(").replace("]", ")"); String valuesStr = values.toString().replace("[", "(").replace("]", ")"); // 7. 执行SQL语句 Connection conn = DbUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql.append(columns).append("value").append(valuesStr).toString()); int i = ps.executeUpdate(); return i; } /** * 更新指定对象T在数据库中的记录。 * 此函数首先构造SQL更新语句,通过反射获取对象的字段信息, * 并根据@TableField和@TableId注解来处理主键和列名。 * * @param t 需要更新的对象,必须包含@Table和@TableField注解 * 以指示对应的数据库表名和列名 * @return 更新操作影响的行数,成功则返回大于0的值,失败或无影响则返回0 * @throws Exception 可能抛出的异常,包括但不限于SQLException、IllegalAccessException */ public int update(T t) throws Exception { // 构建SQL更新语句 StringBuffer sql = new StringBuffer("update "); // 获取对象的类,从而得到表名 Class<?> aClass = t.getClass(); String tableName = aClass.getSimpleName(); TableName annotation = aClass.getAnnotation(TableName.class); if (annotation != null) { tableName = annotation.value(); } sql.append(tableName + " set "); // 添加WHERE子句的前缀 String where = " where "; // 遍历对象的所有字段 Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields) { // 获取字段名 String name = field.getName(); // 处理@TableField注解,用于自定义列名 TableField fieldAnnotation = field.getAnnotation(TableField.class); if (fieldAnnotation != null) { name = fieldAnnotation.value(); } // 设置字段访问权限并获取字段值 field.setAccessible(true); Object value = field.get(t); // 处理@TableId注解,用于主键条件 TableId tableId = field.getAnnotation(TableId.class); if (tableId != null) { String tableIdName = tableId.value(); where += tableIdName + "='" + value + "'"; continue; } // 添加列名=值的组合到SQL语句中 sql.append(name + "='" + value + "',"); } // 删除SQL语句末尾多余的逗号 sql.deleteCharAt(sql.length() - 1).append(where); // 获取数据库连接并准备执行SQL语句 Connection conn = DbUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql.toString()); // 执行更新操作并返回影响的行数 return ps.executeUpdate(); } //删除框架 private Class<T> clazz; public BaseDao(){ //this表示子类Dao对象 Class<? extends BaseDao> aClass = this.getClass(); //获取当前子类的父类的反射类 ParameterizedType genericSuperclass =(ParameterizedType)aClass.getGenericSuperclass(); //获取该反射类中的泛型类型 Type actualTypeArguments = genericSuperclass.getActualTypeArguments()[0]; clazz=(Class)actualTypeArguments; } //delete from 表名 where 主键列名=主键值 public int delete(Object id) throws Exception{ StringBuffer sql = new StringBuffer("delete from "); String tableName = clazz.getSimpleName();//实体类的名称 TableName annotation = clazz.getAnnotation(TableName.class); if(annotation!=null){ tableName = annotation.value(); } sql.append(tableName+" where "); Field[] declaredFields = clazz.getDeclaredFields(); for (Field field: declaredFields) { TableId tableId = field.getAnnotation(TableId.class); if(tableId!=null){ sql.append(tableId.value()+"="+id); break; } } //执行sql语句 Connection conn = DbUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql.toString()); int i = ps.executeUpdate(); return i; } }