自定义ORM框架
手撕ORM框架
1.创建Maven工程
(https://img2024.cnblogs.com/blog/3475901/202407/3475901-20240711102718483-1421959314.png)
)
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的反射
Class<? extends BaseDao> aClass = this.getClass();
// 获取当前子类的父类反射
ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
// 获取反射类中的泛型
Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
// 将获取到的泛型类型经过强转后给 clazz
clazz = (Class) actualTypeArgument;
}
/**
* 插入
* @param t
* @return
*/
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;
}
/**
* 根据主键更新
* @param t
* @return
*/
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;
}
/**
* 根据 Id 删除
* @param id
* @return
*/
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;
}
}
// 执行 sql
Connection connection = DbUtil.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
int i = preparedStatement.executeUpdate();
return i;
} catch (Exception e){
e.printStackTrace();
}
return 0;
}
/**
* 根据 Id 查询
* @param id
* @return
*/
public T selectById(Object id) {
// 设计 sql
StringBuffer sql = new StringBuffer("select * from ");
// 获取表名
// 获取类的简名
String tableName = clazz.getSimpleName();
// 获取TableName注解
TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
if(tableNameAnnotation != null){
tableName = tableNameAnnotation.value();
}
// 拼接上表名
sql.append(tableName + " where ");
// 获取主键的列名
Field[] declaredFields = clazz.getDeclaredFields();
// 遍历
for (Field declaredField : declaredFields) {
TableId tableId = declaredField.getAnnotation(TableId.class);
if(tableId != null){
sql.append(tableId.value() + " = " + id);
break;
}
}
// 执行 sql 语句
Connection connection = DbUtil.getConnection();
// 获取 PreparedStatement 对象
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql.toString());
// 获取结果集
ResultSet resultSet = preparedStatement.executeQuery();
// 封装到实体类
while(resultSet.next()){
T t = clazz.newInstance();
for (Field field : declaredFields) {
field.setAccessible(true);
// 获取带有主键注解的列
TableId tableId = field.getAnnotation(TableId.class);
// 获取带有表属性的列
TableField tableField = field.getAnnotation(TableField.class);
// 获取数据库列名 如果没有注解就默认使用属性名字
String name = field.getName();
if(tableId != null){
name = tableId.value();
}
if(tableField != null){
name = tableField.value();
}
// 获取数据库中指定列的值
Object o = resultSet.getObject(name);
// 为指定对象的属性赋值
field.set(t,o);
}
return t;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
/**
* 查询所有
* @return
*/
public List<T> selectAll(){
// 存储查到的信息
List<T> list = new ArrayList<>();
// 设计 sql
StringBuffer sql = new StringBuffer("select * from ");
// 获取表名
// 获取类的简名
String tableName = clazz.getSimpleName();
TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
if(tableNameAnnotation != null){
tableName = tableNameAnnotation.value();
}
sql.append(tableName);
// 执行 sql 语句
Connection connection = DbUtil.getConnection();
try {
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
// 获取结果集
ResultSet resultSet = preparedStatement.executeQuery();
Field[] declaredFields = clazz.getDeclaredFields();
// 封装数据
while(resultSet.next()){
T t = clazz.newInstance();
// 为实体类属性赋值
for (Field field : declaredFields) {
// 开启私有属性访问
field.setAccessible(true);
TableId tableId = field.getAnnotation(TableId.class);
TableField tableField = field.getAnnotation(TableField.class);
// 获取属性名
String name = field.getName();
if(tableId != null){
name = tableId.value();
}
if(tableField != null){
name = tableField.value();
}
// 获取数据库中指定列的值
Object o = resultSet.getObject(name);
// 为指定对象的属性赋值
field.set(t,o);
}
// 将封装好的对象加入到集合中
list.add(t);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return list;
}
}
8.用户定义Dao使用BaseDao
public class StudentDao extends BaseDao <Student>{}
public class TeacherDao extends BaseDao <Teacher>{}
9.测试
public class Test {
@org.junit.Test
public void test01(){
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(1);
student.setName("李白");
student.setAge(18);
studentDao.insert(student);
}
@org.junit.Test
public void test02(){
// 测试更新
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(5);
student.setName("李白");
student.setAge(30);
studentDao.update(student);
}
@org.junit.Test
public void test03(){
// 测试删除
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(1);
studentDao.deleteById(student.getId());
}
@org.junit.Test
public void test04(){
// 测试根据 Id 查询
StudentDao studentDao = new StudentDao();
System.out.println(studentDao.selectById(5));
}
@org.junit.Test
public void test05(){
// 测试查询全部
StudentDao studentDao = new StudentDao();
System.out.println(studentDao.selectAll());
}
}