传统JDBC下的开发
在使用传统JDBC下的开发我们是这样的。
比如数据库中有这么一个表
如果我们要去读取这个表中的数据我们会这样做
public class App001 {
static public void main(String[] argvArray) throws Exception {
(new App001()).start();
}
private void start() throws Exception {
// 加载 Mysql 驱动
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
// 数据库连接地址
String dbConnStr = "jdbc:mysql://localhost:3306/ormtest?user=root&password=Yad@121413";
// 创建数据库连接
Connection conn = DriverManager.getConnection(dbConnStr);
Statement stmt = conn.createStatement();
// 创建 SQL 查询
String sql = "select * from user limit 20000";
// 执行查询
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
// 创建新的实体对象 手动硬编码赋值
UserEntity entity = new UserEntity();
entity._userId = rs.getInt("id");
entity._userName = rs.getString("user_name");
}
// 获取结束时间
long t1 = System.currentTimeMillis();
// 关闭数据库连接
stmt.close();
conn.close();
}
}
可以看出直接使用传统硬编码的方式虽然代码上很直观,但是当操作对象修改了以后我们就要重新修改查询的代码这很呆。
此时我们就要请出我们的大将---反射
用反射改良后的代码
1.首先我们创建一个注解用来帮助和数据库中的字段进行映射
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
}
2.为实体类添加注解和数据库中的资源进行映射
public class DogEntity {
@Column(name = "id")
public int id ;
@Column(name = "name")
public String name ;
@Column(name = "size")
public int size ;
}
3.创建一个EntityHelper来帮助生成对象
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
public class EntityHelper {
/**
*根据传入的对象的Class,动态的生成对象并为对象赋值
* @param clazz 要实例化生成的对象的Class
* @param rs 根据SQL查询出来的结果集
* @return
* @throws SQLException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public Object create(Class<?> clazz,ResultSet rs) throws SQLException, IllegalAccessException, InstantiationException {
//如果rs 和 clazz 为空就直接返回
if (null==rs || clazz==null ) return null;
//根据Class实例化对象
Object entity = clazz.newInstance();
//获取对象的属性字段[]
Field[] fArray = entity.getClass().getDeclaredFields();
//遍历属性 并为加了注解的属性赋值
for (Field f : fArray){
//对没有加注解的属性过滤
Column annoCloumn = f.getAnnotation(Column.class);
if (annoCloumn==null) continue;
//获取在注解中的赋值,也就是对在表中的字段名称
String colName = annoCloumn.name();
//拿到值
Object colVal =rs.getObject(colName);
if (colVal==null ) continue;
//给对象对应的属性赋值
f.set(entity,colVal);
}
return entity;
}
}
这样子我们的代码就变样子了
public class App001 {
static public void main(String[] argvArray) throws Exception {
(new App001()).start();
}
private void start() throws Exception {
//这里都是JDBC通用代码是不变的
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
String dbConnStr = "jdbc:mysql://47.104.232.174:3306/ormtest?user=root&password=Yad@121413";
Connection conn = DriverManager.getConnection(dbConnStr);
Statement stmt = conn.createStatement();
String sql = "select * from dog limit 20000";
ResultSet rs = stmt.executeQuery(sql);
//在这里我们使用EntityHelper 来帮助生成
EntityHelper helper = new EntityHelper();
while (rs.next()) {
// 根据传入的Class对象创建新具体的对应的实体对象
//这里就没有了传统的硬编码灵活性高了很多
DogEntity entity = (DogEntity) helper.create(DogEntity.class,rs);
}
// 关闭数据库连接
stmt.close();
conn.close();
}
}
可以发现查询的代码和原来的硬编码有了很大的差别,传统硬编码一个操作只对应一个对象,现在我们可以灵活的改变对象的类型EntityHelper会为我们动态的生成对象。
看到这里可能会有一直豁然开朗的感觉,我们可以想象到Mybatis这一类的框架原来在下面为我们做的事情大概是怎么完成的,为什么我们没有写对应的赋值语句框架就为我们完成了对象的实例化和赋值操作。
但是!!!! 请注意我们这里用的是JDK的反射,每一次要去生成对象的时候我们都会通过反射entity.getClass().getDeclaredFields(); 来获得属性,这是很消耗性能的,会导致执行效率上和硬编码差很多
后面我就会如何提高反射的效率来帮助我们制作一个既没有硬编码的重复和繁琐,又没有使用反射所导致的性能下降的ORM工具。