Java-027-ORM

ORM和Hibernate

数据建模方式

  1. 面向对象的思考方式 – 更加抽象, 逻辑性更强

    • 需要处理的实体, 他们之间的关系, 互相的操作
  2. 关系数据库 – 数据的存储结构

    • 数据库表, 表的字段 (属性), 数据记录

SQL

面向对象 SQL

JDBC代码回顾

基本过程

  1. 创建jdbc连接器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @Component("statement")
    public class StatementFactory implements FactoryBean<Statement> {
    // TODO: 把数据库地址放到配置文件里
    private static final String DB_PATH = "jdbc:sqlite:resources/sample.db";

    @Override
    public Statement getObject() throws Exception {
    // 创建数据库连接
    Connection connection = DriverManager.getConnection(DB_PATH);
    Statement statement = connection.createStatement();
    statement.setQueryTimeout(30);

    return statement;
    }

    @Override
    public Class<?> getObjectType() {
    return Statement.class;
    }

    @Override
    public boolean isSingleton() {
    return true;
    }
    }
    • 如何创建数据库的连接器?
    • 如何去连接数据库? 如何销毁数据库连接?
    • 如何管理数据库连接? 连接池, 连接缓存

      所以和SQL数据库打交道的程序都需要考虑这些通用问题 —> 考虑重用: 库, 框架

  2. 创建DAO (Data Access Object)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    @Component
    public class ProductDao {
    private Statement statement;

    public ProductDao(Statement statement) {
    this.statement = statement;
    }

    public Product get(int id) {
    try {
    String query = "SELECT * FROM `product` WHERE id = " + id;
    ResultSet rs = statement.executeQuery(query);

    if (rs.next()) {
    return new Product(
    rs.getInt("id"),
    rs.getString("name"),
    rs.getString("description"),
    rs.getDouble("price")
    );
    } else {
    return null;
    }
    } catch (SQLException e) {
    System.out.println("Failed to query product from DB.");
    }

    return null;
    }
    }
  3. 为每个具体的数据查询操作创建SQL语句

    1
    2
    String query = "SELECT * FROM `product` WHERE id = " + id;
    ResultSet rs = statement.executeQuery(query);
    • 准备好SQL语句
    • 调用JDBC的API传入SQL语句,设置参数
    • 解析JDBC返回的结果
    1. 代码内部的数据结构和操作 –> SQL语句
    2. SQL语句返回的结果 –> Java的对象

注意事项–ORM解决的问题是什么

  • 数据DAO和数据库连接创建的解耦

思考: 如果迁移数据库到MySQL, Java代码需要如何修改?

  1. 修改数据库的链接. URL变了, 验证方式
  2. SQL语句修改 —> 工作量就很大! —> 能不能用比SQL更抽象的数据模型去封装? —> 对象!
    SQL注入

ORM (Object Relational Mapping) 对象关系映射

顾名思义, ORM把关系数据库中的数据结构映射到面向对象的结构. ORM是一种以面向对象的方式对关系数据库进行操作的技术.

为什么需要ORM

没有ORM

使用JDBC操作关系数据库

  • 准备好SQL语句
  • 调用JDBC的API传入SQL语句,设置参数
  • 解析JDBC返回的结果
  • 这个过程中, 我们的Java代码和SQL是强耦合的, 我们需要专门处理Java里的数据结构和SQL语句的转换, 然后还需要解析SQL返回的结果.
分层 –> 插入中间层: 把SQL给封装起来 –> Driver/dialect –> “接口” (ORM) <– Java代码只依赖于

这部分隐藏着很多重复的逻辑 看到这个, 是不是有一股要重构和优化的冲动!

使用ORM的方式

  • 根据具体业务和领域, 根据面向对象, - 参考关系数据库的模型, 设计代码的数据模型. Modeling
  • 使用ORM在Java代码中声明和定义数据结构, 也就是Java的类和对象.
  • 使用ORM提供的抽象接口和方法, 声明和定义对数据的访问层. 注意: 这一层不是直接操作SQL 思考: 有什么好处?
  • ORM框架根据我们的定义 (面向对象方式), 构建相应的SQL语句和环境, 执行对数据的访问

优势

  • 数据抽象层次高, 和现实业务更接近
  • 数据访问操作的抽象层次高, ORM框架帮助隐藏了很多细节
  • 面向对象封装

劣势

  • 相对固定的数据模型
  • 不如直接使用SQL灵活
  • 性能

Spring boot + Hibernate (ORM)

Maven的依赖

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.25.2</version>
</dependency>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.zsoltfabok/sqlite-dialect -->
<dependency>
<groupId>com.zsoltfabok</groupId>
<artifactId>sqlite-dialect</artifactId>
<version>1.0</version>
</dependency>

数据库连接相关的配置

项目/src/main/resources/application.properties

1
2
3
4
5
6
7
8
server.port = 8080

# Database settings
spring.datasource.url=jdbc:sqlite:resources/sample.db
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver.class=org.sqlite.JDBC
hibernate.dialect=org.hibernate.dialect.SQLiteDialect

Model的代码

如何去定义数据的对象的类 (Schema)

  • 注解:
1
2
3
4
5
6
7
8
@Entity
@Table(name = "product")
public class Product {}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

@Column(name = "name")
  • Dao的代码

如何去查询

1
@Repository

CrudRepository使用

CrudRepository

报错信息 Error creating bean with name ‘entityManagerFactory’

如果你遇到

1
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
  • 解决办法:
  • pom.xml 加上依赖
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.2.Final</version>
</dependency>

代码链接