反射实例:ORM搭建
程序员文章站
2022-04-23 23:40:02
...
反射机制是Java给开发者留下的后门之一,让开发者可以通过API去动态获得被封装的属性和方法,很多框架的功能都是基于反射实现的,像Hibernate、Mybatis、Spring(这个是真的多)。今天就手写一个ORM最基础的实现,也就是Java对象和数据库表之间的映射。
项目使用SpringBoot搭建,其实用什么都一样,只不过SpringBoot本身配置都写好了,创建也快,习惯了= =
pom.xml里引入MySQL的依赖
创建实体类,其中@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表,对应实体类
然后是三个注解,其中@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