【ORM思想】源码手写篇:仿Hibernate手写ORM框架

ORM思想之源码手写篇:仿Hibernate手写ORM框架


前面我们通过跟踪源码的方式剖析了 Hibernate 和 MyBatis 两个框架是如何应用 ORM 思想的,接下来我们自己定义一个简单的 ORM 框架(仿Hibernate),希望能通过这种方式让大家亲自零距离的去应用一下 ORM。

1. ORM 框架的结构设计

在这里插入图片描述

  1. 第一层为配置层:
  • orm.cfg.xml 是框架的核心配置文件,主要用来设置数据库连接信息和映射配置文件路径信息
  • Xxx.mapper.xml 是框架的映射配置文件,主要用来设置类和表之间以及属性和字段之间的映射关系
  • Xxx.java 是带有映射注解的实体类,主要用来设置类和表之间以及属性和字段之间的映射关系,和 Xxx.mapper.xml 的作用一样,只不过采用的是注解方式,两者二选一
  1. 第二层为解析层:
  • XmlUtils 类用来解析 orm.cfg.xml 和 Xxx.mapper.xml 两个配置文件的数据
  • AnnotationUtils 类用来解析实体类中的映射注解
  1. 第三层为封装层:
  • Config 类用来封装和存储从 orm.cfg.xml 文件中解析得到的数据
  • Mapper 类用来封装和存储从 Xxx.mapper.xml 或实体类中解析得到的映射数据
  1. 第四层为构建层:
  • Configuration 类用来读取核心配置文件并存放在 Config类,使用Config类来构建SessionFactory
  • SessionFactory 类用来创建 Connection 连接, 解析映射文件封装到Mapper里,用来获取 Session 对象
  1. 第四层为功能层:
  • SqlUtils 类主要是根据 entity 和 Mapper 来生成SQL语句
  • Session 类通过对 JDBC 的封装最终实现增删改查功能

2. ORM 框架的代码实现

2.1 创建 Maven 项目

<groupId>top.lzchao.framework.orm</groupId>
<artifactId>orm-core</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

2.2 编写 pom.xml

<dependencies>
   <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.12.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

2.3 设计注解 @Entity,@Table,@Id, @Colunm

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 识别为关系映射类
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity { }
/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 用来设置当前类和哪个表对应
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String name() default "";
}
/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 用来设置当前属性和表中哪个字段对应
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name();
}
/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 用来设置哪个属性对应的字段是主键
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id { }

2.4 XmlUtils 类

XmlUtils 类是一个基于 Jsoup 的工具类 ,主要用来解析 orm.cfg.xml 和 Xxx.mapper.xml

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 解析XML文件工具类
 */
public class XmlUtils {
    /**
     * 通过文件的路径获取 xml 的 document 对象
     * @param filePath 文件的路径
     * @return 返回文档对象
     */
    public static Document getXMLByFilePath(String filePath) {
        if (null == filePath) { // 空值处理
            throw new RuntimeException("【框架异常】核心配置文件路径错误!");
        }
        try {
            // 通过Jsoup.parse获取Document对象
            return Jsoup.parse(new File(filePath), "utf-8");
        } catch (IOException e) {
            throw new RuntimeException("【框架异常】核心配置文件路径错误!");
        }
    }
    /**
     * 针对 orm.cfg.xml 文件,获得数据库信息并存到 Map 集合中
     * @param document xml 文档对象
     * @return
     */
    public static Map<String, String> property2Map(Document document) {
        Elements elements = document.getElementsByTag("property");// 获取所有的<property>标签
        Map<String, String> propConfig = new HashMap<>();// 创建一个Map集合存放和核心配置文件中的信息
        for (Element element : elements) {
            String key = element.attr("name"); // 获取key值
            String value = element.html(); // 获取value
            propConfig.put(key, value); // 存入集合
        }
        return propConfig;
    }
    /**
     * 针对核心文件下的 mapping 标签,获取 映射文件 路径到 Set 集合中
     * @param document xml 文档对象
     * @param attrName 属性名 package 和 resource
     * @return
     */
    public static Set<String> mapping2Set(Document document, String attrName) {
        Elements elements = document.getElementsByAttribute(attrName); //获取带有attrName属性的标签
        Set<String> mappingSet = new HashSet<>(); // 创建一个值不重复的Set集合存放 映射文件路径
        for (Element element : elements) {
            mappingSet.add(element.attr(attrName));
        }
        return mappingSet;
    }
    /**
     * 针对映射文件的某标签获取所有实体映射信息
     * @param document
     */
    public static Map<String, String> mapping2Map(Document document) {
        HashMap<String, String> map = new HashMap<>(); // 创建map集合存放实体类映射信息
        Map<String, String> ids = mappingId2Map(document); // 获取映射信息id的集合
        map.putAll(ids); // 合并集合
        Elements elements = document.getElementsByTag("property"); // 获取所有<property>标签的
        for (Element element : elements) {
            Element child = element.child(0); // 获取标签下面的第一个子节点 <column>
            map.put(element.attr("name"), child.attr("name")); // 存入map集合中
        }
        return map;
    }
    /**
     * 针对映射文件的某标签获取 实体类 id映射信息
     * @param document
     */
    public static Map<String, String> mappingId2Map(Document document) {
        Elements elements = document.getElementsByTag("id"); // 获取id标签节点
        HashMap<String, String> map = new HashMap<>(); //存放id映射信息的集合
        for (Element element : elements) {
            Element child = element.child(0);
            map.put(element.attr("name"), child.attr("name"));
        }
        return map;
    }
}

2.5 AnnotationUtils 类

AnnotationUtil 类主要用来通过反射技术解析实体类中的注解,从而获得映射数据,源码

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 注解解析工具
 */
public class AnnotationUtil {
    /**
     * 判断是否被表示为实体类
     * @param entityClass
     * @return
     */
    public static boolean isEntity(Class entityClass) {
        return entityClass.isAnnotationPresent(Entity.class);
    }
    /**
     * 获取路径名
     * @param clz
     * @return
     */
    public static String getClassName(Class clz) {
        return clz.getName();
    }
    /**
     * 获取表名
     * @param entityClass
     * @return
     */
    public static String getTableName(Class entityClass) {
        if (entityClass.isAnnotationPresent(Table.class)) { //是否存在Table注解
            Table annotation = (Table) entityClass.getAnnotation(Table.class);//获取Table注解
            return annotation.name();//取值
        } else {
            throw new RuntimeException("缺少 Table 注解");
        }
    }
    /**
     * 得到主键属性和对应的字段
     * @param entityClass
     * @return
     */
    public static Map<String, String> getIdMapper(Class entityClass) {
        Map map = new HashMap<String, String>(); // 存放id映射信息的集合
        Field[] fields = entityClass.getDeclaredFields(); // 获取所有字段
        boolean flag = true;
        for (Field field : fields) { // 遍历字段
            if (field.isAnnotationPresent(Id.class)) { // 如果是带有Id注解的字段
                flag = false;
                if (field.isAnnotationPresent(Column.class)) {
                    String fieldName = field.getName(); // 获取字段名字
                    Column ormColumn = field.getAnnotation(Column.class); //获取Column注解
                    String columnName = ormColumn.name(); //取值获得列名
                    map.put(fieldName, columnName); //存入集合
                    break;
                } else {
                    throw new RuntimeException("【框架异常】缺少 Column 注解");
                }
            }
        }
        if(flag){
            throw new RuntimeException("【框架异常】缺少 Id 注解");
        }
        return map;
    }
    /**
     * 得到类中所有属性和对应的字段
     * @param entityClass
     * @return
     */
    public static Map<String, String> getPropMapping(Class entityClass) {
        Map map = new HashMap<String, String>(); // 创建集合存放 属性和列名 的映射关系
        Field[] fields = entityClass.getDeclaredFields(); // 获取所有字段
        for (Field field : fields) { // 遍历字段
            if (field.isAnnotationPresent(Column.class)) { // 是否被标注Column注解
                String fieldName = field.getName(); // 获取属性名
                Column ormColumn = field.getAnnotation(Column.class);
                String columnName = ormColumn.name(); // 获取列名
                map.put(fieldName, columnName); // 存入集合
            } else {
                throw new RuntimeException("【框架异常】缺少 Column 注解");
            }
        }
        return map;
    }
}

2.6 Mapper 类

Mapper 类用来封装并存储从 Xxx.mapper.xml 中或从实体类中解析得到的映射信息,哪个表和哪个类映射,哪个字段和哪个属性映射等等

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description:
 */
public class Mapper {
    private String className; //类名
    private String tableName; //表名
    private Map<String, String> idMapper = new HashMap(); //主键字段和属性
    private Map<String, String> propMapping = new HashMap(); //非主键字段和属性
}

2.7 Config 类

Config 类主要用来存储 orm.cfg.xml 配置文件中的信息

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 封装核心配置信息
 */
public class Config {
    private Map<String, String> propConfig; //核心配置文件数据
    private Set<String> mappingSet; //映射配置文件
    private Set<String> entitySet; //实体类
    public Map<String, String> getPropConfig() {
        return propConfig;
    }
    public Set<String> getMappingSet() {
        return mappingSet;
    }
    public Set<String> getEntitySet() {
        return entitySet;
    }
    public void configure(String cfgName) {
        // 1.获取类路径
        String classpath = Config.class.getClassLoader().getResource("").getPath();
        // 2.解析配置文件
        File file = new File(classpath + cfgName);
        if(file.exists()){ // 文件存在
            Document document = XmlUtils.getXMLByFilePath(file.getPath()); // 开始解析XML文件
            propConfig = XmlUtils.property2Map(document); 
            mappingSet = XmlUtils.mapping2Set(document, "resource");
            entitySet = XmlUtils.mapping2Set(document, "class");
        } else {
            throw new RuntimeException("【框架异常】核心配置文件未找到");
        }
    }
}

2.8 Configuration类

Configuration 类用来读取核心配置文件并存放在 Config 类并且使用 Config 类来构SessionFactory

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: Configuration 类
 */
public class Configuration {
    public Config config;
    public Configuration(){
        config = new Config();
    }
    public Configuration configure(){
        return configure("orm.cfg.xml");
    }
    public Configuration configure(String cfgName){
        config.configure(cfgName); //读取配置文件
        return this;
    }
    public SessionFactory buildSessionFactory(){
        return buildSessionFactory(config);
    }
    public SessionFactory buildSessionFactory(Config config){
        SessionFactory sessionFactory = new SessionFactory();
        sessionFactory.setPropConfig(config.getPropConfig());
        try{
        	//解析映射文件
            sessionFactory.getMapping(config.getMappingSet(), config.getEntitySet());
        } catch (ClassNotFoundException e){
            throw new RuntimeException(e.getMessage());
        }
        return sessionFactory;
    }
}

2.9 SessionFactory 类

类用来创建 Connection 连接, 解析映射文件封装到Mapper里,用来获取 Session 对象

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: SessionFactory 类
 */
public class SessionFactory {
    private Map<String, String> propConfig; //核心配置文件数据
    private List<Mapper> mapperList; //实体映射信息
    public void setPropConfig(Map<String, String> propConfig) {
        this.propConfig = propConfig;
    }
    /**
     * 从 propConfig 获得信息,连接数据库
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    private Connection getConnection(boolean autoCommit) {
        // 底层JDBC获取连接操作
        String url = propConfig.get("connection.url");
        String driverClass = propConfig.get("connection.driverClass");
        String username = propConfig.get("connection.username");
        String password = propConfig.get("connection.password");
        try {
            Class.forName(driverClass);
            Connection connection = DriverManager.getConnection(url, username, password);
            connection.setAutoCommit(autoCommit); // 设置自动提交事务
            return connection;
        } catch (Exception e) {
            throw new RuntimeException("【框架异常】数据库连接错误!");
        }
    }
    /**
     * 获取所有实体映射关系
     * @param mappingSet
     * @param entitySet
     */
    public void getMapping(Set<String> mappingSet, Set<String> entitySet) throws ClassNotFoundException {
        List<Mapper> mapperList = new ArrayList<>();
        for (String xmlPath : mappingSet) {
            String classpath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
            Document document = XmlUtils.getXMLByFilePath(classpath + xmlPath); // 解析 mapper.xml 文件
            Element element = document.getElementsByTag("class").get(0);// 获取class 根元素
            Map<String, String> ids = XmlUtils.mappingId2Map(document);
            Map<String, String> properties = XmlUtils.mapping2Map(document);
            Mapper mapper = new Mapper(); // 创建Mapper对象并封装
            mapper.setIdMapper(ids);
            mapper.setPropMapping(properties);
            mapper.setClassName(element.attr("name"));
            mapper.setTableName(element.attr("table"));
            mapperList.add(mapper); //添加集合
        }
        for (String classPath : entitySet) {
            Class entityClass = Class.forName(classPath);
            if(AnnotationUtil.isEntity(entityClass)){ // 如果标准@Entity注解说明要解析
                String className = AnnotationUtil.getClassName(entityClass); // 获取类名
                String tableName = AnnotationUtil.getTableName(entityClass); // 获取表名
                Map<String, String> ids = AnnotationUtil.getIdMapper(entityClass);
                Map<String, String> mapping = AnnotationUtil.getPropMapping(entityClass);
                Mapper mapper = new Mapper();// 创建Mapper对象并封装
                mapper.setClassName(className);
                mapper.setTableName(tableName);
                mapper.setIdMapper(ids);
                mapper.setPropMapping(mapping);
                mapperList.add(mapper); //添加集合
            }
        }
        this.mapperList = mapperList;
    }
    public Session openSession(){
        return this.openSession(true);
    }
    public Session openSession(boolean autoCommit){
        return new Session(getConnection(autoCommit), mapperList);
    }
}

2.10 SqlUtils 类

通过 实体类字节码 和 映射信息对象 来生成对应的SQL语句

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: 生成SQL语句
 */
public class SqlUtils {
    /**
     * insert into 表名 (字段列表 ... ) values(字段列表 ...)
     * @param entity
     * @param mapper
     * @return
     */
    public static String insertSQL(Object entity, Mapper mapper) throws IllegalAccessException {
        StringBuilder insertSQL = new StringBuilder();
        insertSQL.append("insert into " + mapper.getTableName() + " (");
        StringBuilder colunmNames = new StringBuilder("");
        StringBuilder colunmValues = new StringBuilder("");
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true); // 暴力访问
            String colunmName = mapper.getPropMapping().get(field.getName()); // 获取列名
            colunmNames.append(colunmName + ", ");
            String colunmValue = field.get(entity).toString(); // 获取值
            colunmValues.append("'" + colunmValue + "', ");
        }
        insertSQL.append(colunmNames.substring(0, colunmNames.lastIndexOf(","))).append(")").append(" values(").append(colunmValues.substring(0, colunmValues.lastIndexOf(","))).append(")");
        return insertSQL.toString();
    }
    /**
     * update 表名 set 列名1 = 值1, ... where 列id = 值id
     * @param entity
     * @param mapper
     * @return
     */
    public static String updateSQL(Object entity, Mapper mapper) throws IllegalAccessException {
        StringBuilder updateSQL = new StringBuilder();
        updateSQL.append("update " + mapper.getTableName() + " set ");
        StringBuilder colunm = new StringBuilder();
        StringBuilder id = new StringBuilder();
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String colunmId = mapper.getIdMapper().get(field.getName());
            if(colunmId == null){
                String colunmNames = mapper.getPropMapping().get(field.getName());
                String colunmValue = field.get(entity).toString(); // 获取值
                colunm.append(colunmNames + " = '" + colunmValue + "', ");
            } else {
                String idVal = (String) mapper.getIdMapper().values().toArray()[0];
                id.append(idVal + " = '" + field.get(entity) + "'");
            }
        }
        updateSQL.append(colunm.substring(0, colunm.lastIndexOf(","))).append(" where ");
        updateSQL.append(id);
        return updateSQL.toString();
    }
    /**
     * delete from 表名 where 表主键 = 值
     *
     * @param entity
     * @param mapper
     * @return
     */
    public static String deleteSQL(Object entity, Mapper mapper) throws IllegalAccessException, NoSuchFieldException {
        StringBuilder deleteSQL = new StringBuilder();
        deleteSQL.append("delete from " + mapper.getTableName() + " where ");
        String idProp = (String) mapper.getIdMapper().keySet().toArray()[0];
        String idColumn = (String) mapper.getIdMapper().values().toArray()[0];
        // 5. 得到主键的值
        Field field = entity.getClass().getDeclaredField(idProp);
        field.setAccessible(true);
        String idVal = field.get(entity).toString();
        deleteSQL.append(idColumn + " = '" + idVal + "'");
        return deleteSQL.toString();
    }
    /**
     * select * from 表名 where 表主键 = 值
     *
     * @param entity
     * @param mapper
     * @return
     */
    public static String findOneSQL(Class entity, Object id, Mapper mapper) throws IllegalAccessException, NoSuchFieldException {
        StringBuilder findOneSQL = new StringBuilder();
        findOneSQL.append("select * from " + mapper.getTableName() + " where ");
        String idColumn = (String) mapper.getIdMapper().values().toArray()[0];
        findOneSQL.append(idColumn + " = " + id);
        return findOneSQL.toString();
    }
}

2.11 Session

类通过对 JDBC 的封装最终实现增删改查功能

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: Session
 */
public class Session {
    private Connection conn; // 连接对象
    List<Mapper> mapperList; // 映射信息对象集合
    public Session(Connection conn, List<Mapper> mapperList) {
        this.conn = conn;
        this.mapperList = mapperList;
    }
    /**
     * 保存用户
     * @param entity
     */
    public void save(Object entity) {
        String insertSQL = null;
        for (Mapper mapper : mapperList) { // 遍历映射信息集合
            if (mapper.getClassName().equals(entity.getClass().getName())) { //找到与实体类对应的映射信息
                try {
                    insertSQL = SqlUtils.insertSQL(entity, mapper); // 通过工具类生成SQL语句
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        // 把 sql 语句打印到控制台
        System.out.println("【ORM-save】" + insertSQL);
        // 通过 JDBC 发送并执行 sql
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(insertSQL);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
     * 更新用户
     * @param entity
     */
    public void update(Object entity) {
        String updateSQL = null;
        for (Mapper mapper : mapperList) { // 遍历映射信息集合
            if (mapper.getClassName().equals(entity.getClass().getName())) { //找到与实体类对应的映射信息
                try {
                    updateSQL = SqlUtils.updateSQL(entity, mapper); // 通过工具类生成SQL语句
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        // 把 sql 语句打印到控制台
        System.out.println("【ORM-update】" + updateSQL);
        // 通过 JDBC 发送并执行 sql
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(updateSQL);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据id删除用户
     * @param entity
     */
    public void delete(Object entity) {
        String deleteSQL = null;
        for (Mapper mapper : mapperList) { // 遍历映射信息集合
            if (mapper.getClassName().equals(entity.getClass().getName())) { //找到与实体类对应的映射信息
                try {
                    deleteSQL = SqlUtils.deleteSQL(entity, mapper); // 通过工具类生成SQL语句
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e.getMessage());
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        // 把 sql 语句打印到控制台
        System.out.println("【ORM-delete】" + deleteSQL);
        // 通过 JDBC 发送并执行 sql
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(deleteSQL);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据id查询
     * @param clz 字节码
     * @param id 主键值
     */
    public <T> T get(Class<T> clz, Object id) {
        String findOneSQL = null;
        for (Mapper mapper : mapperList) { // 遍历映射信息集合
            if (mapper.getClassName().equals(clz.getName())) { //找到与实体类对应的映射信息
                try {
                    findOneSQL = SqlUtils.findOneSQL(clz, id, mapper); // 通过工具类生成SQL语句
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e.getMessage());
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        // 把 sql 语句打印到控制台
        System.out.println("【ORM-select】" + findOneSQL);
        // 通过 JDBC 发送并执行 sql
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(findOneSQL);
            ResultSet rs = statement.executeQuery();
            Object obj = null;
            if(rs.next()){
                obj = clz.newInstance();
                for(Mapper mapper:mapperList){
                    if (mapper.getClassName().equals(clz.getName())) {
                        Map<String, String> propMapping = mapper.getPropMapping();
                        for (String key : propMapping.keySet()) {
                            String column = propMapping.get(key);
                            Object value = rs.getObject(column);
                            Field field = clz.getDeclaredField(key);
                            field.setAccessible(true);
                            field.set(obj, value);
                        }
                        break;
                    }
                }
            }
            rs.close();
            statement.close();
            return (T) obj;
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 释放资源
     */
    public void close() {
        if (null != conn) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3. ORM 框架的测试使用

手写 ORM框架 主要是为了来体现 ORM思想,并不是为了开发一个成熟的持久层框架出来,因此很多逻辑并不完善,很多情况也未去考虑,接下来测试下框架

3.1 创建 Maven工程

在这里插入图片描述

3.2 编写 pom.xml

<dependencies>
    <dependency>
        <groupId>top.lzchao.framework.orm</groupId>
        <artifactId>orm-core</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

我们前面把 ORM-core 框架打成 jar 包并 install 到了本地 Maven 仓库中,因此在使用该框架时需要从本地仓库进行加载

3.3 orm.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<orm-configuration>
    <property name="connection.url">jdbc:mysql://localhost:3306/test?serverTimezone=UTC</property>
    <property name="connection.driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="connection.username">root</property>
    <property name="connection.password">123456</property>

    <mapping class="top.lzchao.orm.entity.Book"/> <!--带有映射注解的实体类-->
    <mapping resource="top/lzchao/orm/entity/Book.hbm.xml"/> <!--映射配置文件-->
</orm-configuration>

这是框架的核心配置文件,我们既采用了 xml 方式配置映射数据,也采用了注解方式在实体类中配置映射数据,最后可以二选一分别进行功能测试。

实体类和映射配置文件

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: Book 实体类
 */
@Entity
@Table(name = "t_book")
public class Book {
    @Id
    @Column(name = "bid")
    private int id; //主键
    @Column(name = "bname")
    private String name; //图书名字
    @Column(name = "author")
    private String author; //图书作者
    @Column(name = "price")
    private double price; //图书价格
    // getter、setter...
    // toString...
<?xml version='1.0' encoding='utf-8'?>
<orm-mapping>
    <class name="top.lzchao.orm.entity.Book" table="t_book">
        <id name="id">
            <column name="bid"/>
        </id>
        <property name="name">
            <column name="bname"/>
        </property>
        <property name="author">
            <column name="author"/>
        </property>
        <property name="price">
            <column name="price"/>
        </property>
    </class>
</orm-mapping>

注意:对于同一个表或实体类,不需要既进行 xml 配置,又进行注解配置,二选一即可,这里同时进行配置只是为了测试方便。

4. 测试类

/**
 * @author: LzCc
 * @blog: https://blog.csdn.net/qq_41744145
 * @description: ORM框架测试类
 */
public class BookDao {
    private SessionFactory factory;
    @Before
    public void init() {
        //1. 创建一个 Configuration 对象,解析 hibernate 的核心配置文件
        Configuration cfg = new Configuration().configure("orm.cfg.xml");
        //2. 创建 SessinFactory 对象,解析映射信息并生成基本的 sql
        factory = cfg.buildSessionFactory();
    }
    @Test
    public void testSave() {
        //3. 得到 Session 对象,该对象具有增删改查的方法
        Session session = factory.openSession();
        //4. 保存数据
        Book book = new Book();
        book.setId(1);
        book.setName("Spring5核心原理");
        book.setAuthor("TOM");
        book.setPrice(119.9);
        session.save(book);
        //5. 释放资源
        session.close();
    }
    @Test
    public void testFindOne() {
        //3. 得到 Session 对象,该对象具有增删改查的方法
        Session session = factory.openSession();
        //4. 查询数据
        Book book = session.get(Book.class, 1);
        System.out.println(book);
        //5. 释放资源
        session.close();
    }
    @Test
    public void testUpdate() {
        //3. 得到 Session 对象,该对象具有增删改查的方法
        Session session = factory.openSession();
        //4. 更新数据
        Book book = new Book();
        book.setId(1);
        book.setName("Spring5核心原理");
        book.setAuthor("TOM");
        book.setPrice(69.9);
        session.update(book);
        //5. 释放资源
        session.close();
    }
    @Test
    public void testDelete() {
        //3. 得到 Session 对象,该对象具有增删改查的方法
        Session session = factory.openSession();
        //4. 删除数据
        Book book = new Book();
        book.setId(1);
        session.delete(book);
        //5. 释放资源
        session.close();
    }
}

save():
在这里插入图片描述
update():
在这里插入图片描述
findOne():
在这里插入图片描述
delete():
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值