首页 > Java框架应用 > Hibernate 入门教程 > Hibernate 自动进行数据封装

Hibernate 自动进行数据封装

1. 前言

Hibernate 可以构建各种复杂的 SQL 语句,但其本质都是反射机制结合映射关系完成的。

框架也仅是一款程序产品,人为编写的产物。要相信,只要你愿意,你完全可以实现自己的 JDBC 框架。

本节课和大家继续聊聊 Hibernate 是如何自动封装数据的。

2. 理想状态

程序中的数据通过 SQL 语句传递给数据库,从 IO 流的角度上理解是数据的输出。

把数据库中的数据封装成程序能识别、使用的类型,这个过程叫数据库封装。从 IO 流的角度上讲是数据的输入。

数据封装的实现还是利用反射机制。

数据封装的思路也简单。

表结构对应类结构,一行记录或说一个实体对应一个对象,字段对应属性。还是同样的配方。

即将编写实例之前,先做一个理想化的假设:

  • 表名和类名相同;
  • 表中的字段名和类中的属性名相同;
  • 实体类中没有特别复杂的数据类型。

理想状态设定便于大家由浅入深理解实现过程。

真实项目中,很难达到理想化状态 。因为 JAVA 程序和关系型数据库的命名规范有差异性,不管谁迁就谁,都会让彼此难受。

在上一节课的自定义 Session 对象中添加一个封装方法:

public class MySession<T> {
public T get(Class<T> clz, Serializable id) throws Exception {
    String sql = createSql_(clz, id);
    // JDBC 常规操作…… 得到结果集
    ResultSet rs=this.getRs(sql);
    //数据封装
    T entity= this.wrap(clz, rs);   
    return entity;
}
/**
*构建 SQL ,具体代码参考本系列的《Hibernate 自动生成SQL语句》 
*/
private String createSql(Class<T> clz, Serializable id) {
    return null;
}
/**
*得到结果集,此方法就是常规的 JDBC 操作
*/
private ResultSet getRs(String sql) {
    return  null;
}
/**
*数据封装
*/
private T  wrap(Class<T> clz,ResultSet rs) throws InstantiationException, IllegalAccessException {
    return null;
}
}

关注 wrap()方法即可,直接让代码说话:

private T  wrap(Class<T> clz,ResultSet rs) throws InstantiationException, IllegalAccessException {
    // 通过反射创建对象
    T entity = clz.newInstance();
    String attName = null;
    Object value = null;
    // 检查结果集中是否存在数据
    if (rs.next()) {
        // 属性信息
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            // 表的字段名即类的属性名
            attName = field.getName();
            // 从结果集获取值
            value = rs.getObject(attName);
            //允许直接访问私有属性
            field.setAccessible(true);
            //数据封装
            field.set(entity, value);
        }
    }
    return entity;
}

上面代码没有进行类型转换相关操作,依靠 JAVA 自动类型转换完成,对于常用的基本类型,不会出多大问题。但对于较复杂的数据类型,需要编码进行强制类型转换。

下面这 2 行代码,通过打破私有属性的壁垒,直接给属性赋值。只适合用于验证、测试环境下。

field.setAccessible(true);
field.set(entity, value);

测试一下代码:

Student stu = new MySession<Student>().get(Student.class, new Integer(1));
System.out.println(stu);

在一切理想的状态下,一个简易的 JDBC 框架就开始工作了。

虽然它很薄弱,且不堪重负,但是,它是黑暗中的一道光,能指导你认识框架本质。

3. 非理想状态

再来一个非理想状态下的实现:

  • 类名与表名不同名;
  • 类中的属性名与表中的字段名有不同的命名;

整体思想和上面代码没有区别,通过属性名找到字段名,因为属性名和字段名不相同,所以需要通过属性上面的注解信息得到字段名。

让代码自己说话:

private T wrap_(Class<T> clz, ResultSet rs) throws InstantiationException, IllegalAccessException, SQLException,NoSuchMethodException, SecurityException, IllegalArgumentException,InvocationTargetException {
    // 使用反射创建对象
    T entity = clz.newInstance();
    // 属性名
    String attName = null;
    // 列名
    String columnName = null;
    Object value = null;
    Column columnAnnotaiton = null;
    // 检查结果集中是否存在数据
    if (rs.next()) {
        // 属性信息
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            // 属性名
            attName = field.getName();
            // 查看属性上面的注解
            columnAnnotaiton = field.getAnnotation(Column.class);
            if (columnAnnotaiton != null) {
                columnName = columnAnnotaiton.name();
            } else {
                columnName = attName;
            }
            // 从结果集中获取值
            value = rs.getObject(columnName);
            // 属性的 set 方法名
            String setMethodName = "set" + attName.substring(0, 1).toUpperCase() + attName.substring(1);
            // 属性的 set 方法
            Method setMethod = clz.getDeclaredMethod(setMethodName, new Class[] { field.getType() });
            setMethod.invoke(entity, new Object[] { value });
            }
        }
        return entity;
    }

上面代码一样没有考虑复杂类型存在情况。

4. 小结

本节课程和大家聊了聊 Hibernate 是如何自动实现数据封装的,从实现原理上讲,还是比较容易理解的。

但是,真实环境的需求总是千变万化的,理想只存于理想中。

Hibernate 的实现和我们实现的最大区别在于,它总是能适应不同的需求,要适应不同的需求,无论是代码的设计还是编码都会有难度。

但是,千里之行,始于脚下,一步一步,终能看透所有的本质。

本文来自互联网用户投稿,不拥有所有权,该文观点仅代表作者本人,不代表本站立场。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,邮箱:80764001@qq.com,予以删除。
© 2023 PV138 · 站点地图 · 免责声明 · 联系我们 · 问题反馈