山一程-- repository -- ORM -- Hibernate

前路:ORM 减少 OO 与 RDBMS 阻抗不匹配  2021-03-08


参考书籍:

1.《prospring5》 chapter 7 


1. Hibernate 能够建立多种关联模型,最常见得关联是一对多和多对多,

一对多关联(ORM术语来说,一对多关联用于对数据结构中得零对多关系和一对多关系进行建模)


多对多映射

DB 有一个连接表 SINGER_INSTRUMENT

  


Spring boot 2 配置 MySql 和 Hibernate

Maven dependencies

 1  <dependency>
 2 
 3         <dependency>
 4             <groupId>org.springframework.boot</groupId>
 5             <artifactId>spring-boot-starter-data-jpa</artifactId>
 6             <version>2.4.1</version>
 7         </dependency>
 8 
 9          <dependency>
10             <groupId>mysql</groupId>
11             <artifactId>mysql-connector-java</artifactId>
12             <scope>runtime</scope>
13             <version>8.0.21</version>
14         </dependency>
15         <dependency>
16             <groupId>org.hibernate</groupId>
17             <artifactId>hibernate-core</artifactId>
18             <version>6.0.0.Alpha6</version>
19             <type>pom</type>
20         </dependency>
21         <dependency>
22             <groupId>org.hibernate</groupId>
23             <artifactId>hibernate-entitymanager</artifactId>
24             <version>6.0.0.Alpha6</version>
25             <type>pom</type>
26         </dependency>
27         <dependency>
28             <groupId>org.hibernate.orm</groupId>
29             <artifactId>hibernate-ehcache</artifactId>
30             <version>6.0.0.Alpha6</version>
31         </dependency>
32 
33         <dependency>
34             <groupId>org.hibernate.validator</groupId>
35             <artifactId>hibernate-validator</artifactId>
36             <version>7.0.0.Alpha6</version>
37         </dependency>        
View Code

Hibernate Session Factory 扩展了EntityManagerFactory, 因此可以用于创建 SessionFactory 和 EntityManager,

Spring 在 LocalSessionFactoryBean中添加了对上述功能的支持,可将原生Hibernate代码与 JPA 代码混合使用。

配置一个 SessionFactory bean, 可以使用 LocalSessionFactoryBean 实例。

需要一个 DataSource 以便与数据库建立连接,

需要一种方言来创建SQL,

需要知道实体在哪里, componentScan.

不需要添加HibernateTransactionManager,默认配置的JpaTransactionManager可以处理事务,

但是JPA 在事务传播级别上的限制,HT不受此限制。则要添加。

SessionFactory 中的 getCurrentSession() 获取实例。

可以创建EntityManager。 故可以用 CustomerRepository. 

 


Hibernate 与其他ORM 工具,比如JDO 和 JPA 一样都是围绕对象模型进行设计的。

定义了映射之后,不再需要构建SQL与数据库进行交互,HQL来定义查询,Hibernate 会转换为SQL.

HQL 需要考虑的是对象方面而不是数据库方面。

 

 2021-03-09 00:31:41


 2021-03-09

Hibernate 默认是延迟 lazily 获取关联数据, Hibernate 不会连接关联表,出于性能考虑。

要获取,两种方式:

1.可以使用获取模式 EAGER 来定义关联,例如 @ManyToMany( fetch = FetchType.EAGER)。Hibernate 在每个查询中获取关联记录.

2. 强制Hibernate 在需要时获取查询中的关联记录。 如果使用 Criteria 查询,则可以调用 Criteria.setFetchMode() 函数来指示急切获取关联。

当使用 NamedQuery 时,可以使用 Fetch 操作符指示急切获取关联。

NamedQuery 可以被外化为 XML 文件或者在 实体类中使用注解来声明。

3. repository 调用

 


findById ( Long id )

多个参数可用, Query 接口的 setParametersList() 或 setParameters() 方法。


更新数据。 save

当在映射中定义了 cascade = CascadeType.ALL时,删除操作将删除歌手记录及其所有相关信息。


 由于无法控制所生成的SQL,因此在定义映射时要特别小心,关联和获取策略,

要观察生成的SQL语句。

Hibernate 如何管理会话的内部机制也很重要,特别是在批处理操作中。

Hibernate 将在会话中托管被管理对象,并定期刷新和清除它们,

设计不佳的数据访问逻辑会导致Hibernate 频繁刷新会话,并极大地影响性能。

设置 批处理大小,获取大小 在调整Hibernate 性能方面起重要作用,应该在会话工厂中定义它们,

并在加载测试APP时调整它们以获取最佳值。


2021-03-10

JPA  对 ORM编程模型进行标准化,定义了 JPA 持久化程序应该实现的一组 通用概念, 注解, 接口和其他服务.

使用 Hibernate 作为标准 Java 持久化的持久化提供程序。

JPA实体管理器: Spring 所支持的 EntityManagerFactory 的类型以及该配置, LocalContainerEntityManagerFactoryBean.


核心概念是 EntityManager 接口,来自EntityManagerFactory 类型的工厂。

EM 主要工作是维护一个持久化上下文,在该上下文中存储由起管理的所有实体实例。

EM的配置被定义为一个持久化单元,且在应用程序中可以有多个持久化单元。

Hibernate,可像使用 Session 接口一样使用持久上下文。


EntityManagerFactory 等同于 SessionFactory。

Hibernate 中,托管实体从存储在会话中,可以通过 Hibernate 的 SessionFactory 或 Session 接口直接与会话进行交互。

但是,在 JPA 中,不能直接与吃计划上下文进行交互。需要依靠 EM来完成工作。


JPA2 引入了 JPQL ,引入了强类型的 Criteria API , 依赖映射实体的元数据来构造查询。任何错误将在编译时而不是运行时被发现.



 

JPA Hibernate EM 配置

 

 

 

 1 @Configuration
 2 @EnableTransactionManagement
 3 @ComponentScan(basePackages = {"org.techroad.onestep"})
 4 public class JpaConfig {
 5 
 6     public static Logger logger = LoggerFactory.getLogger(JpaConfig.class);
 7 
 8     @Bean
 9     public PlatformTransactionManager transactionManager(DataSource dataSource){
10         return new JpaTransactionManager(entityManagerFactory(dataSource));
11     }
12 
13     @Bean
14     public EntityManagerFactory entityManagerFactory(DataSource dataSource){
15         LocalContainerEntityManagerFactoryBean factoryBean =
16                 new LocalContainerEntityManagerFactoryBean();
17         factoryBean.setPackagesToScan("org.techroad.onestep");
18         factoryBean.setDataSource(dataSource);
19         factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
20         factoryBean.setJpaProperties(hibernateProperties());
21         factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
22         factoryBean.afterPropertiesSet();
23         return factoryBean.getNativeEntityManagerFactory();
24     }
25 
26     @Bean
27     public Properties hibernateProperties(){
28         Properties properties = new Properties();
29         properties.put("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
30         properties.put("hibernate.format_sql",true);
31         properties.put("hibernate.use_sql_comments",true);
32         properties.put("hibernate.show_sql",true);
33         properties.put("hibernate.max_fetch_depth",3);
34         properties.put("hibernate.jdbc.batch_size",10);
35         properties.put("hibernate.jdbc.fetch_size",50);
36         return properties;
37     }
38 
39     @Bean
40     public JpaVendorAdapter jpaVendorAdapter(){
41         return new HibernateJpaVendorAdapter();
42     }
43 }
 1 import javax.persistence.*;
 2 import java.util.Date;
 3 import java.util.HashSet;
 4 import java.util.Set;
 5 
 6 import static javax.persistence.GenerationType.IDENTITY;
 7 
 8 @Entity
 9 @Table(name = "singer")
10 @NamedQueries(
11         {
12                 @NamedQuery(name = Singer.FIND_ALL, query = "select s from Singer s"),
13                 @NamedQuery(name = Singer.FIND_SINGER_BY_ID, query = "select distinct s from Singer s " +
14                         "left join fetch s.albums a " +
15                         "left join fetch s.instruments i " +
16                         "where s.id = :id"),
17                 @NamedQuery(name = Singer.FIND_ALL_WITH_ALBUM, query = "select distinct s from Singer s " +
18                         "left join fetch s.albums a " +
19                         "left join fetch s.instruments i")
20         }
21 )
22 @SqlResultSetMapping(
23         name = "singerResult",
24         entities = @EntityResult(entityClass = Singer.class)
25 )
26 public class Singer {
27     public static final String FIND_ALL = "Singer.findAll";
28     public static final String FIND_SINGER_BY_ID = "Singer.findById";
29     public static final String FIND_ALL_WITH_ALBUM = "Singer.findAllWithAlbum";
30 
31     @Id
32     @GeneratedValue(strategy = IDENTITY)
33     @Column(name = "ID")
34     public long id;
35 
36     @Version
37     @Column(name = "VERSION")
38     public int version;
39 
40     @Temporal(TemporalType.DATE)
41     @Column(name = "BIRTH_DATE")
42     public Date birthDate;
43 
44     @Column(name = "FIRST_NAME")
45     public String firstName;
46 
47     @Column(name = "LAST_NAME")
48     public String lastName;
49 
50     @OneToMany(mappedBy = "singer", cascade = CascadeType.ALL, orphanRemoval = true)
51     private Set<Album> albums = new HashSet<>();
52 
53     @ManyToMany
54     @JoinTable(name = "singer_instrument",
55             joinColumns = @JoinColumn(name = "SINGER_ID"),
56             inverseJoinColumns = @JoinColumn(name = "INSTRUMENT_ID"))
57     private Set<Instrument> instruments = new HashSet<>();
58 
59 
60     @Override
61     public String toString() {
62         return "Singer{" +
63                 "id=" + id +
64                 ", version=" + version +
65                 ", birthDate=" + birthDate +
66                 ", firstName='" + firstName + '\'' +
67                 ", lastName='" + lastName + '\'' +
68                 '}';
69     }
70 }
View Code


 

 

 1 @Entity
 2 @Table(name="album")
 3 public class Album implements Serializable {
 4 
 5     public Album(){}
 6 
 7     @Id
 8     @GeneratedValue(strategy= GenerationType.IDENTITY)
 9     @Column(name="ID")
10     private long id;
11 
12     @Version
13     @Column(name="version")
14     private int version;
15 
16     @Column(name="title")
17     private String title;
18 
19     @Temporal(TemporalType.DATE)
20     @Column(name="RELEASE_DATE")
21     private Date releaseDate;
22 
23     @ManyToOne
24     @JoinColumn(name="SINGER_ID")
25     private Singer singer;
26 
27     public Album(String title,Date releaseDate){
28         this.title = title;
29         this.releaseDate = releaseDate;
30     }
31 
32     public long getId() {
33         return id;
34     }
35 
36     public void setId(long id) {
37         this.id = id;
38     }
39 
40     public int getVersion() {
41         return version;
42     }
43 
44     public void setVersion(int version) {
45         this.version = version;
46     }
47 
48     public String getTitle() {
49         return title;
50     }
51 
52     public void setTitle(String title) {
53         this.title = title;
54     }
55 
56     public Date getReleaseDate() {
57         return releaseDate;
58     }
59 
60     public void setReleaseDate(Date releaseDate) {
61         this.releaseDate = releaseDate;
62     }
63 
64     public Singer getSinger() {
65         return singer;
66     }
67 
68     public void setSinger(Singer singer) {
69         this.singer = singer;
70     }
71 }
View Code

查询单个对象。

 


2021-03-11

 查询非类型化结果

向数据库提交查询 并随意操作结果。而不是将它们存储在映射的实体类中.

基于网络的报告,可以列出跨多个表格的一定数量的列。

用来显示歌手信息和最近发布的专辑名称的网页。摘要信息包含Singer完整姓名及其最近发布的专辑目的。

没有专辑的Singer将不会被列出.

可以通过一个查询并手动操作ResultSet 对象来实现此目的。


 2021-03-12

 

 


save 或更新数据

save() 方法首先通过检查 id 值来检查对象是否是新的实体实例。

如果 id 为 null (即尚未分配),那么该对象就是一个新的实体实例,此时 调用 EntityManager.persist() 方法.

当调用 persist() 方法时,EntityManager 将持久化该实体实例并使其成为当前持久化上下文的托管实例.

如果 id 值存在,则表示正在执行更新操作.将调用 EM.merge() 方法。

调用merge() 方法时,EM将实体的状态合并到当前的持久化上下文中.


 

 Native query

对提交给数据库的查询进行绝对控制,特定于数据库的查询被称为本地查询。

使用JPA 本地查询的一个主要好处是能将 ResultSet 映射回 ORM 映射的实体类。

 

 

 

 


JPA2 Criteria API进行条件查询

APP 为用户提供前端来搜索信息,应用程序显示了大量可搜索的字段,而用户只在在其中的一些字段中输入信息且进行搜索。

要准备大量搜索是很 困难的,用户可以选择不同的参数组合。

Criteria API 查询就派上用场了。

JPA2 引入的一个主要新功能就是强类型的 Criteria API 查询. 

在新的 Criteria API 中,传递到查询中的条件是基于映射的实体类的元模型。因此,所指定的每个条件都是强类型的。并且在

编译而不是运行时会发现错误。

JAP Criteira API 中,实体类的元模型由具有下划线 (_ ) 后缀的实体类名表示.

元模型类用@StaticMetamodel 注解,属性是映射的实体类,类中是每个属性及其相关类型的声明。

可使用相关工具根据实体类中的JPA映射自动生成这些元模型类,Hibernate 元模型生成器。

<!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-jpamodelgen -->
<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-jpamodelgen</artifactId>
    <version>6.0.0.Alpha6</version>
</dependency>

配置 IDEA 启动自动生成

  


JAP Config

 1 @Configuration
 2 @EnableTransactionManagement
 3 @ComponentScan(basePackages = {"org.techroad.onestep"})
 4 public class JpaConfig {
 5 
 6     public static Logger logger = LoggerFactory.getLogger(JpaConfig.class);
 7 
 8     @Bean
 9     public PlatformTransactionManager transactionManager(DataSource dataSource){
10         return new JpaTransactionManager(entityManagerFactory(dataSource));
11     }
12 
13     @Bean
14     public EntityManagerFactory entityManagerFactory(DataSource dataSource){
15         LocalContainerEntityManagerFactoryBean factoryBean =
16                 new LocalContainerEntityManagerFactoryBean();
17         factoryBean.setPackagesToScan("org.techroad.onestep");
18         factoryBean.setDataSource(dataSource);
19         factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
20         factoryBean.setJpaProperties(hibernateProperties());
21         factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
22         factoryBean.afterPropertiesSet();
23         return factoryBean.getNativeEntityManagerFactory();
24     }
25 
26     @Bean
27     public Properties hibernateProperties(){
28         Properties properties = new Properties();
29         properties.put("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
30         properties.put("hibernate.format_sql",true);
31         properties.put("hibernate.use_sql_comments",true);
32         properties.put("hibernate.show_sql",true);
33         properties.put("hibernate.max_fetch_depth",3);
34         properties.put("hibernate.jdbc.batch_size",10);
35         properties.put("hibernate.jdbc.fetch_size",50);
36         return properties;
37     }
38 
39     @Bean
40     public JpaVendorAdapter jpaVendorAdapter(){
41         return new HibernateJpaVendorAdapter();
42     }
43 }
View Code

 


 


Spring Data JPA

企业级应用中,关键业务数据,始终需要保留每个实体的版本.

每次插入,更新或删除客户记录时,应将先前版本保存在历史记录 或审计表中,以满足公司的审计或其他合规要求.

1. 创建数据库触发器,在执行任何更新操作前将更新前的记录克隆到历史记录表中。

2. 在数据访问层(例如 使用 AOP ) 开发相关的逻辑。

3. Hibernate Envers (entity versioning system) 用于实现实体版本的自动化。

两种审计策略

1. 默认策略:Envers 维护一个用于修改记录的列,每次插入或更新记录时,会使用从数据库序列或表中检索到的版本号将新记录插入到历史记录表中。

2.有效性审计策略:该策略将存储历史记录的开始和最终版本。每次插入或更新记录时,都会使用开始版本号将新记录插入到历史记录表中。同时,以前的

记录用最终版本号进行更新。也可以配置 Envers,

有效性策略会触发更多的数据库更新,但检索历史记录的速度会更快. 最终版本时间戳也被写入历史记录,在查询数据时可以更容易在特定时间点识别记录的快照.

1. 为实体版本控制添加表

1. 对于实体将进行版本控制的每个表,都需要创建相应的历史记录表; 对于 SINGER_AUDIT 表中记录的版本控制,则创建一个名为SINGER_AUDIT_H 的历史记录表。

 

 

Hibernate 还需要另一个表来跟踪版本号和创建每个版本的时间戳。表名应该是 REVINFO


 

 EntityManagerFactory

Hibernate Envers 以 EJB监听器的形式出现,在 LocalContainerEntityManagerFactory bean 中配置这些监听器.

Envers 审计事件监听器, org.hibernate.envers.event.AuditEventListener 被附加到各种持久化事件中。

监听器将拦截 post-insert, post-update 或 post-delete 事件,并将实体类的更新前快照克隆到历史记录中。

还被附加到那些关联的更新事件 pre-collection-update, pre-collection-remove 和 pre-collection-recreate,以便

处理与实体类关联的更新操作。

Envers 能够保持关联(例如 一对多关联 或 多对多 关联)中实体的历史,


启用实体版本控制,使用@Audited 注解实体即可,可在类级别使用,对所有字段的更改进行审计。忽略的字段上使用@NotAudited.

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>6.0.0.Alpha6</version>
</dependency>

 


Spring boot JPA 启动器依赖 Spring Boot JPA, 带有 Hibernate 来抽象持久化层。

Spring Repository 接口会被自动检测到。

开发人员提供实体,存储库扩展 以及将二者结合在一起使用的 Application 类。还可以开发一个类来填充数据库。 


 


Spring boot 配置 JPA

 

 

 

 1 CREATE TABLE SINGER(
 2 ID INT NOT NULL AUTO_INCREMENT,
 3 FIRST_NAME VARCHAR(60) NOT NULL,
 4 LAST_NAME VARCHAR(40) NOT NULL,
 5 BIRTH_DATE DATE,
 6 VERSION INT NOT NULL DEFAULT 0,
 7 UNIQUE UQ_SINGER_1 (FIRST_NAME, LAST_NAME),
 8 PRIMARY KEY(ID)
 9 );
10 
11 CREATE TABLE ALBUM(
12 ID INT NOT NULL auto_increment,
13 SINGER_ID INT NOT NULL,
14 TITLE VARCHAR(100) NOT NULL,
15 RELEASE_DATE DATE,
16 VERSION INT NOT NULL DEFAULT 0,
17 UNIQUE UQ_ALBUM_1 (SINGER_ID, TITLE),
18 PRIMARY KEY(ID),
19 CONSTRAINT FK_ALBUM_SINGER_1 foreign key (SINGER_ID)
20 REFERENCES SINGER(ID)
21 );
22 
23 CREATE TABLE INSTRUMENT(
24 INSTRUMENT_ID VARCHAR(20) NOT NULL,
25 PRIMARY KEY(INSTRUMENT_ID)
26 );
27 
28 CREATE TABLE SINGER_INSTRUMENT(
29 SINGER_ID INT NOT NULL,
30 INSTRUMENT_ID VARCHAR(20) NOT NULL,
31 PRIMARY KEY(SINGER_ID, INSTRUMENT_ID),
32 constraint FK_SINGER_INSTRUMENT_1 foreign key (SINGER_ID)
33 REFERENCES SINGER(ID) ON DELETE cascade,
34 constraint    FK_SINGER_INSTRUMENT_2 foreign key(INSTRUMENT_ID)
35 REFERENCES INSTRUMENT(INSTRUMENT_ID)
36 );

 JPA 是完整且功能强大的ORM数据访问标准,第三方库 Spring data JPA, Hibernate Envers 可实现各种横切关注点。

如果需要绝对控制查询,可使用 JPA 本地查询支持,不要使用 JDBC.

JPA 来实现数据访问层。 使用 Hibernate 作为持久化服务提供程序来配置 JPA 的 EntityManagerFactory.

本地查询 和 强类型的 JPA Criteria API.

Spring Data JPA 的 Repository 抽象 实体监听器来跟踪实体类的基本审计信息。

2021-03-14 01:31:58


2021-04-10

 


 

posted @ 2021-03-08 22:34  君子之行  阅读(163)  评论(0)    收藏  举报