手撕ORM框架
1.创建Maven工程
![image-20240702211254563]()
2.导入依赖
<dependencies><!-- 引入 jdbc 的依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!-- 引入数据源的依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency></dependencies>
3.设计操作数据库的工具类
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();}}public static Connection getConnection(){try {Connection connection = dataSource.getConnection();return connection;} catch (SQLException e) {e.printStackTrace();} finally {}return null;}public static void closeAll(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){if(resultSet != null){try {resultSet.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if(preparedStatement != null){try {preparedStatement.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if (connection != null){try {connection.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}
}
4.创建配置文件 db.properties
#数据源信息
url=jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai
username=root
password=123456
driverClass=com.mysql.cj.jdbc.Driver
5.设计自定义注解
// 修饰表名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {String value();
}
// 修饰主键Id
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {String value() default "id";
}
// 修饰表字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {String value();
}
6.实体类中使用自定义注解
@TableName("表名")
@Data
public class Student {@TableId("主键名")private Integer id;@TableField("列名")private String name;@TableField("列名")private Integer age;
}
7.设计BaseDao
public class BaseDao <T> {private Class<T> clazz;public BaseDao(){// this表示子类 Dao对象Class<? extends BaseDao> aClass = this.getClass();// 获取当前子类的父类反射类ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();// 获取该反射类中的泛型类型Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];// 将获取得到的泛型类型经过强转后重新给 clazzclazz = (Class) actualTypeArgument;}public int insert(T t) {try{// 设计通用的添加// 1.首先写出添加的 sql insert into 表名(字段名) values(属性名)// 这里注意 into 后面的空格StringBuffer sql = new StringBuffer("insert into ");// 2.获取实体类所对应的表名// 2.1自定义注解 @TableName 标识实体类所对应的表// 2.2拿到实体类的反射类,利用反射获取注解,进而获得表的名称Class clazz = t.getClass();// 实体类的名称String simpleName = clazz.getSimpleName();// 表的名称String tableName = "";// 获取类上的注解TableName annotation = (TableName) clazz.getAnnotation(TableName.class);if(annotation != null){tableName = annotation.value();}// 追加拿到的表名sql.append(tableName);// 3.获取所有的列名和对应的属性名// 3.1用来存储数据库中对应的列List<String> columnNames = new ArrayList<String>();// 3.2用来存储需要插入的值List<Object> values = new ArrayList<Object>();// 4.获取所有的属性对象Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 4.1获取类中的属性名String name = field.getName();// 4.2获取属性上的注解// 获取主键的注解TableId tableId = field.getAnnotation(TableId.class);// 获取成员的注解TableField tableField = field.getAnnotation(TableField.class);// 4.3根据拿到的值进行过滤if(tableId != null){// 4.3.1 此时拿到的是主键的注解 退出本轮循环continue;}if(tableField != null){// 4.3.2 此时拿到的是成员的注解// 覆盖掉类中的属性名 使用表中的字段名name = tableField.value();}// 对私有属开启暴力反射field.setAccessible(true);// 获取对象属性对应的值Object value = field.get(t);// 将值经过添加引号后加入到值list中values.add("'"+value+"'");// 将属性列加入到列list中columnNames.add(name);}// 5.将列list转换成字符串,然后使用字符串替换方法将其转换成需要的格式String columnList = columnNames.toString().replace("[","(").replace("]", ")");// 6.将值list转换成字符串,然后使用字符串替换方法将其转换成需要的格式String valueList = values.toString().replace("[","(").replace("]",")");// 7.合并字符串sql.append(columnList + " values " + valueList);System.out.println(sql);// 8.执行插入语句Connection connection = DbUtil.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());int i = preparedStatement.executeUpdate();return i;} catch (Exception e){e.printStackTrace();}return 0;}public int update(T t){try{//设计通用的修改// 1.设计 sql update 表名 set 属性名=值,...StringBuffer sql = new StringBuffer("update ");// 2.获取表名// 2.1获取对象的反射类实例Class clazz = t.getClass();// 2.2拿到对象对应的表名 类的简名String tableName = clazz.getSimpleName();TableName tableNameAnnotation = (TableName) clazz.getAnnotation(TableName.class);if (tableNameAnnotation != null){// 2.3拿到表的名称 使用注解的名称进行覆盖tableName = tableNameAnnotation.value();}// 3.将拿到的表名追加到sql后面sql.append(tableName + " set ");// 4.设置条件部分String where = "where ";// 5.获取所有的列对象Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 5.1获取所有的属性名String name = field.getName();// 5.2获取属性上的注解// 5.2.1获取主键的注解TableId tableId =(TableId) field.getAnnotation(TableId.class);// 5.2.2获取属性的注解TableField tableField =(TableField) field.getAnnotation(TableField.class);// 6.设置私有属性允许访问field.setAccessible(true);// 7.获取属性的值Object value = field.get(t);// 根据注解的种类进行选择性拼接if(tableId != null){String id = tableId.value();where += id + "='" + value + "'";// 拼接完成退出本轮循环continue;}if(tableField != null){name = tableField.value();}sql.append(name + "='" + value + "',");}// 去除 sql 中最后一个 ,sql.deleteCharAt(sql.length() - 1).append(where);System.out.println(sql);// 执行 sql 语句Connection connection = DbUtil.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());int i = preparedStatement.executeUpdate();return i;} catch (Exception e){e.printStackTrace();}return 0;}public int deleteById(Object id){try {// 设计删除的 sql 语句StringBuffer sql = new StringBuffer("delete from ");// 获取实体类的名称String tableName = clazz.getSimpleName();// 获取表名的注解TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);// 判断是否添加了@TableName注解if(tableNameAnnotation != null){tableName = tableNameAnnotation.value();}// 追加 where条件sql.append(tableName + " where ");// 获取所有的属性对象Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {TableId tableId =(TableId) field.getAnnotation(TableId.class);if(tableId != null){sql.append(tableId.value() + "=" + id);// 跳出循环break;}}// 执行 sqlConnection connection = DbUtil.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());int i = preparedStatement.executeUpdate();return i;} catch (Exception e){e.printStackTrace();}return 0;}}
8.用户定义Dao使用BaseDao
public class StudentDao extends BaseDao <Student>{}
public class TeacherDao extends BaseDao <Teacher>{}
9.测试
public class Test {@org.junit.Testpublic void test01(){// 测试添加StudentDao studentDao = new StudentDao();Student student = new Student();student.setId(1);student.setName("李白");student.setAge(18);studentDao.insert(student);}@org.junit.Testpublic void test02(){// 测试更新StudentDao studentDao = new StudentDao();Student student = new Student();student.setId(1);student.setName("李白");student.setAge(25);studentDao.update(student);}@org.junit.Testpublic void test03(){// 测试删除StudentDao studentDao = new StudentDao();Student student = new Student();student.setId(1);studentDao.deleteById(student.getId());}
}