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

反射实例:ORM搭建

程序员文章站 2022-04-23 23:40:02
...

反射机制是Java给开发者留下的后门之一,让开发者可以通过API去动态获得被封装的属性和方法,很多框架的功能都是基于反射实现的,像Hibernate、Mybatis、Spring(这个是真的多)。今天就手写一个ORM最基础的实现,也就是Java对象和数据库表之间的映射。

项目使用SpringBoot搭建,其实用什么都一样,只不过SpringBoot本身配置都写好了,创建也快,习惯了= =

pom.xml里引入MySQL的依赖

反射实例:ORM搭建

创建实体类,其中@Table、@MyId、@Column三个注解是我自定义的,用来实现ORM映射

package pers.hong.entity;

import java.io.Serializable;
import pers.hong.annotation.Column;
import pers.hong.annotation.MyId;
import pers.hong.annotation.Table;

/**
 * @Description:
 * @Auther: hong
 * @Date: 2018/09/12
 */
@Table(name = "user")
public class User implements Serializable{
    @MyId(name="id",isAuto=false)
    private Long id;
    @Column(name="user_name")
    private String userName;
    @Column(name="user_password")
    private String userPassword;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getuserName() {
        return userName;
    }

    public void setuserName(String userName) {
        this.userName = userName;
    }

    public String getuserPassword() {
        return userPassword;
    }

    public void setuserPassword(String userPassword) {
        this.userPassword = userPassword;
    }
}

新建user表,对应实体类

反射实例:ORM搭建

然后是三个注解,其中@Table的作用范围在类上,其他两个在属性上,作用域都是运行时起作用,和Hibernate的注解差不多,@MyId是控制主键是否自增

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String name();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyId {
    boolean isAuto() default true;
    String name();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name();
}

定义dao层接口和实现类

package pers.hong.dao;

import pers.hong.entity.User;

/**
 * @Description:
 * @Auther: hong
 * @Date: 2018/09/12
 */
public interface UserDao {
    void saveUser();
}

@Component
public class UserDaoImpl implements UserDao {

    @Autowired
    private ORMUtil ormUtil;

    @Override
    public void saveUser() {
        User user = new User();
        user.setId(111L);
        user.setuserName("宏宏");
        user.setuserPassword("hong52ni");
        try {
            ormUtil.save(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后就是最重要的ORMUtil类,save()方法就是jdbc的写法,只不过手动输入sql语句变成自动转换,也就是由实体类到表字段的映射,具体实现方法代码都做了注释。数据库连接信息通过properties文件传入,这里有个有意思的地方,如果设置user为username,是会直接读到系统Administrator的,访问不了数据库,所以配置文件的字段也不能瞎写= =

package pers.hong.orm;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import pers.hong.annotation.Column;
import pers.hong.annotation.Table;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description:
 * @Auther: hong
 * @Date: 2018/09/12
 */
@Component
public class ORMUtil {
    @Value("${user}")
    private String user;
    @Value("${password}")
    private String password;
    @Value("${jdbcDriver}")
    private String driver;
    @Value("${url}")
    private String url;

    public void save(Object object) throws Exception {
        //加载驱动
        Class.forName(driver);
        //建立连接
        Connection conn = DriverManager.getConnection(url, user,password);
        //预编译语句
        PreparedStatement pstmt = conn.prepareStatement(prepareSql(object));
        //执行
        pstmt.executeUpdate();
        pstmt.close();
        conn.close();
    }

    private String prepareSql(Object object) throws Exception {
        StringBuffer str = new StringBuffer("INSERT INTO ");
        //反射获得类对象
        Class<?> clazz = object.getClass();
        String tableName = clazz.getSimpleName().toUpperCase();
        //判断是否加了@Table注解
        Table table = clazz.getAnnotation(Table.class);
        if (table != null) {
            tableName = table.name().toUpperCase();
        }
        str.append(tableName + "(");
        //获取字段
        Field[] fields = clazz.getDeclaredFields();
        // 定义存储字段对应值的集合
        List<Object> valueList = new ArrayList<>();
        for (Field field : fields) {
            String fieldName = field.getName().toUpperCase();
            //判断是否使用@Column注解
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                fieldName = column.name().toUpperCase();
            }
            //开启权限
            field.setAccessible(true);
            Object value = field.get(object);
            if (value != null) {
                str.append(fieldName + ",");
                valueList.add(value);
            }
        }
        //删除最后一个逗号
        str.deleteCharAt(str.length() - 1);
        str.append(") VALUES (");
        for (Object value : valueList) {
            Object temp = value;

            // 判断value是否为String类型
            if (value instanceof String) {
                temp = "'"+value+"'";
            }

            str.append(temp+",");
        }
        //删除最后一个逗号
        str.deleteCharAt(str.length() - 1);
        str.append(")");
        return str.toString();
    }
}

测试类,直接调用方法即可


@RunWith(SpringRunner.class)
@SpringBootTest
public class OrmApplicationTests {
    @Autowired
    private UserDaoImpl userDao;

    @Test
    public void contextLoads() {
    }

    @Test
    public void ormValidateTest() {
		userDao.saveUser();
    }
}

以上,通过注解实现的简单ORM映射就写好了,GitHub地址:https://github.com/hong52ni/ORM-demo

 

相关标签: ORM 反射