【JavaEE高级】2-基于JDBC改造, mybatisv1版本

基于JDBC改造, mybatisv1版本

1.综述

  • 需求: 根据用户信息,查询用户列表
  1. 将JDBC中的硬编码,写入properties文件中
  2. 封装查询方法为查询列表函数

代码示例

2.业务流程

public static void main(String[] args) throws Exception {
    // 1 加载配置文件
    loadProperties("mybatis.v1.properties");

    // 2 执行查询
    User query = new User();
    query.setName("测试");
    List<User> users = selectList("queryUserListByName", query);
    System.out.printf("users: %s", users);
}

3.核心代码

  1. 加载配置文件
private static Properties prop = new Properties();

private static void loadProperties(String filename) throws IOException {
    InputStream stream = MybatisV1.class.getClassLoader().getResourceAsStream("demo/" + filename);
    prop.load(stream);
}
  1. 执行查询
// 1 加载驱动
Class.forName(prop.getProperty("db.driver"));

// 2 创建连接
Connection connection = DriverManager.getConnection(
        prop.getProperty("db.url"),
        prop.getProperty("db.user"),
        prop.getProperty("db.password")
);

// 3 SQL 预处理
PreparedStatement ps = connection.prepareStatement(
        prop.getProperty("db.sql." + statementId)
);
// 3.1 获取入参类型
Class<?> parameterType = Class.forName(prop.getProperty("db.sql." + statementId + ".parameterType"));
// 3.2 简单类型 org.apache.ibatis.type.SimpleTypeRegistry
if (SimpleTypeRegistry.isSimpleType(parameterType)) {
    ps.setObject(1, parameter);
} else {
    // 3.3 对象类型
    // 3.3.1 获取有序的参数, 能和 sql 中的 ? 一一对应
    String[] parameters = prop.getProperty("db.sql." + statementId + ".parameters").split(",");
    // 3.3.2 反射取值, 设置到 ps 中
    for (int i = 0; i < parameters.length; i++) {
        String name = parameters[i];
        Field field = parameterType.getDeclaredField(name);
        field.setAccessible(true);
        Object value = field.get(parameter);
        ps.setObject(i + 1, value);
    }
}

// 4 执行 SQL
ResultSet rs = ps.executeQuery();

// 5 获取执行结果
// 5.1 获取结果集类型
Class<?> resultType = Class.forName(prop.getProperty("db.sql." + statementId + ".resultType"));
List<R> resultList = new LinkedList<>();
while (rs.next()) {
    R result = (R) resultType.newInstance();
    ResultSetMetaData metaData = rs.getMetaData();
    for (int i = 1; i < metaData.getColumnCount() + 1; i++) {
        String columnName = metaData.getColumnName(i);
        Field field = resultType.getDeclaredField(columnName);
        field.setAccessible(true);
        field.set(result, rs.getObject(columnName));
    }
    resultList.add(result);
}

// 6 关闭连接
return resultList;
  1. 配置文件
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/jhome?characterEncoding=utf-8
db.user=root
db.password=fc4fcafd-e86d-4fda-9a96-60147e94cc0f

db.sql.queryUserListByName=select * from user where name = ?
# 内部类使用 $ 获取, 因为成的 .class 就是 A.class, A$B.class 这样的结构
db.sql.queryUserListByName.parameterType=com.aizain.jhome.mybatis.source.demo.MybatisV1$User
db.sql.queryUserListByName.resultType=com.aizain.jhome.mybatis.source.demo.MybatisV1$User
db.sql.queryUserListByName.parameters=name

4.关键代码解析

  1. SimpleTypeRegistry
  • org.apache.ibatis.type.SimpleTypeRegistry
  • ibatis 内部有简单类型的 set 集合
SimpleTypeRegistry.isSimpleType(parameterType)
  1. 反射获取/设置 Field
  • Class.getDeclaredField()
    • 获取声明的字段(注意: 是获取字段对象)
    • 可以取得全部字段, getField 只会获取 public 的
      • 不包括父类, getField 会获取父类的,
      • 可通过 getSuperclass().getDeclaredField() 获取父类的
    • Declared 声明的
    • 不可取出 private 的值
  • Class.setAccessible(boolean flag)
    • 设置访问权限
    • 让 Class.getDeclaredField() 可以取出 private 的值
    • 设置前会检查系统安全权限 sm.checkPermission(ACCESS_PERMISSION)
      • java.security.Permission ACCESS_PERMISSION = new ReflectPermission(“suppressAccessChecks”)
      • 设置 java 底层的安全问题, 暂不细纠
// 初始化数据
User user = new User();
user.setName("zain");

// 获取反射 name 字段对象
Class<User> userClazz = User.getClass();
Field nameField = userClazz.getDeclaredField("name");
nameField.setAccessible(true);

// 获取字段值 name = "zain"
Object name = nameField.get(user);

// 设置字段值 user.name = "zain2"
nameField.set(user, "zain2");

你可能感兴趣的:(java,mybatis)