Spring Boot 学习研究笔记(八) - Spring Data JPA与Hibernate区别及配置

一、JPA、Hibernate、Spring Data JPA 的区别与联系

1、什么是 JPA, 它与Hibernate有什么关系?

JPA(Java Persistence API):是Java EE 5的标准ORM接口,也是ejb3规范的一部分。可以通过注解或者XML描述【对象-关系表】之间的映射关系,并将实体对象持久化到数据库中。

JPA为我们提供了以下功能:

  • ORM映射元数据:JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。

如:@Entity、@Table、@Column、@Transient等注解。

  • JPA 的API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。

如:entityManager.merge(T t)。

  • JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

如:from Student s where s.name = ?

 

但是:

JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。所以底层需要某种实现,而Hibernate就是实现了JPA接口的ORM框架。

也就是说:

JPA是一套ORM规范,Hibernate实现了JPA规范。

 

 

 

Hibernate主要是通过三个组件来实现

  • hibernate-core:Hibernate的核心实现,提供了Hibernate所有的核心功能。

  • hibernate-entitymanager:Hibernate实现了标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。

  • hibernate-annotation:Hibernate支持annotation方式配置的基础,它包括了标准的JPA annotation以及Hibernate自身特殊功能的annotation。

 

2、什么是Spring Data JPA?

Spring Data JPA 是Spring 提供的一套简化JPA 开发的框架,按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。同时提供了很多除了CRUD 之外的功能,如分页、排序、复杂查询等。

 

Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的技术实现。如图:

 

3、Spring Data JPA 相关接口:

  • Repository:仅仅只是一个标识,没有任何方法,方便Spring自动扫描识别

  • CrudRepository:继承Repository,实现一组CRUD相关方法

  • PagingAndStortingRepository:继承CrudRepository,实现一组分页排序相关方法

  • JpaRepository:继承PagingAndStortingRepository,实现一组JPA规范方法

 

4、Spring Data JPA 自定义接口约定命名规则:

关键字例子对应的JPQL语句
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstname,findByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNullfindByAgeIsNull… where x.age is null
IsNotNull,NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection age)… where x.age not in ?1
TruefindByActiveTrue()… where x.active = true
FalsefindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase 

 

5、Spring JPA自定义接口解析步骤:

在进行方法名解析时会先去掉多余的前缀,比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析,并且如果方法最后一个参数时 Sort 或 Pageable 类型,也会提取相关信息。

比如:findByNameLikeAndAgeGreaterThan

  • 剔除findBy

  • 判断nameLikeAndAgeGreaterThan(根据POJO规范,首字母变小写)是否为返回对象 User 的一个属性,如果是,则根据该属性进行查询,如果没有该属性,则进行下一步

  • 从右往左截取第一个大写字母开头的字符串(此处为Than),然后检查剩下的字符串是否为 User 的一个属性,如果是,则根据该属性进行查询,否则重复第2步,直到找出 name 为 User 的属性

  • 从已截取的部分后面开始,重新第 1 步开始(剔除条件语句),循环,直到把方法名处理完毕

  • 通过获取的操作、条件和属性、带入参数值,生成查询。

     

二、Spring JPA 配置项

 

在SpringBoot中,Hibernate的相关配置都保存在HibernateProperties,它配置了ConfigurationProperties注解,会自动装载前缀为spring.jpa.hibernate的配置。

 

1、常用配置项

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
​
# 是否开启JPA Repositories,缺省: true
spring.data.jpa.repositories.enabled=true
​
# JPA数据库类型,默认可以自动检测,也能通过设置spring.jpa.database-platform达到同样效果
spring.jpa.database=ORACLE
​
# 数据库平台,常见的值如:
# org.hibernate.dialect.Oracle10gDialect
# org.hibernate.dialect.MySQL5InnoDBDialect
#下文有详细介绍
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
​
# 是否使用JPA初始化数据库,可以在启动时生成DDL创建数据库表,缺省为false
spring.jpa.generate-ddl = false
​
# 更细粒度的控制JPA初始化数据库特性,用来设定启动时DDL操作的类型,下文有详细介绍
# 内嵌数据库 hsqldb, h2, derby的缺省值为create-drop
# 非内嵌数据库的缺省值为none
spring.jpa.hibernate.ddl-auto = update
​
# Hibernate操作时显示真实的SQL, 缺省:false
spring.jpa.show-sql = true
​
# Hibernate 5 隐含命名策略类的全限定名
spring.jpa.hibernate.naming.implicit-strategy= org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl
​
# Hibernate 5 物理命名策略类的全限定名
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 
​
# Use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
spring.jpa.hibernate.use-new-id-generator-mappings= 
​
# 额外设置JPA配置属性,通常配置项是特定实现支持的,如Hibernate常有下面的几条配置项
spring.jpa.properties.* = 
​
# 将SQL中的标识符(表名,列名等)全部使用引号括起来
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
​
# 日志记录执行的SQL
spring.jpa.properties.hibernate.show_sql = true
​
# 是否将格式化SQL日志
spring.jpa.properties.hibernate.format_sql = true
​
# 是否注册OpenEntityManagerInViewInterceptor. 绑定JPA EntityManager 到请求线程中. 默认为: true.
spring.jpa.open-in-view=true
​

 

(1)、spring.jpa.hibernate.ddl-auto 配置

该配置的主要作用是:自动创建、更新、验证数据库表结构,该参数的几种配置如下:

  • create: 每次加载 hibernate 时都会删除上一次生成的表,然后根据 modle 类再重新生成新表,哪怕两次没有任何改变也要这样执行。这也是导致数据库表数据丢失的一个重要原因。

  • create-drop :每次加载 hibernate 时根据 modle 类生成表,但是SessionFactory 一关闭,表就会自动删除。

  • update :最常用的属性,第一次加载hibernate 时,根据modle 类会自动建立表的架构(前提是先建立好数据库),以后加载hibernate 时根据 modle 类自动更新表结构,即使表结构改变了,但表中的行仍然存在,不会删除以前的行。要注意的是:当部署到服务器后,表结构不会马上建立起来。要等应用第一次运行起来才会。

  • validate:每次加载hibernate 时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

     

    ModeReads**import.sql**Alters Database**Structure**Comments
    updateNoYes 
    createYesYesEmpties the database before creating it
    create-dropYesYesDrops the database when the SessionFactory is closed
    validateNoNo 

 

(2)、spring.jpa.properties.hibernate.dialect 配置

hibernate.dialect 属性的属性值为 Hibernate 所支持的 SQL 方言,如下表:

数据库Hibernate方言类
DB2org.hibernate.dialect.DB2Dialect
DB2 AS/400org.hibernate.dialect.DB2400Dialect
DB2 OS390org.hibernate.dialect.DB2390Dialect
PostgreSQLorg.hibernate.dialect.PostgreSQLDialect
MySQLorg.hibernate.dialect.MySQLDialect
MySQL with InnoDBorg.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAMorg.hibernate.dialect.MySQLMyISAMDialect
Oracle(any version)org.hibernate.dialect.OracleDialect
Oracle 9i/10gorg.hibernate.dialect.Oracle9Dialect
Sybaseorg.hibernate.dialect.SybaseDialect
Sybase Anywhereorg.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Serverorg.hibernate.dialect.SQLServerDialect
SAP DBorg.hibernate.dialect.SAPDBDialect
Informixorg.hibernate.dialect.InformixDialect
HypersonicSQLorg.hibernate.dialect.HSQLDialect
Ingresorg.hibernate.dialect.IngresDialect
Progressorg.hibernate.dialect.ProgressDialect
Mckoi SQLorg.hibernate.dialect.MckoiDialect
Interbaseorg.hibernate.dialect.InterbaseDialect
Pointbaseorg.hibernate.dialect.PointbaseDialect
FrontBaseorg.hibernate.dialect.FrontbaseDialect
Firebirdorg.hibernate.dialect.FirebirdDialect

2、命名策略配置

其中Naming是一个内部类,Hibernate字段映射策略就是在这里配置的。

 

根据上面的代码可知,Naming采用下面两个属性:

spring.jpa.hibernate.naming.implicit-strategy= # Hibernate 5 implicit naming strategy fully qualified name.
spring.jpa.hibernate.naming.physical-strategy= # Hibernate 5 physical naming strategy fully qualified name.

现在分别介绍这两个属性分别怎么使用

(1)、physical-strategy的使用:

spring.jpa.hibernate.naming.physical-strategy 它有两个值分别可以配置:

org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

效果分别如下:

​
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl效果等同于:
  hibernate4中的DefaultNamingStrategy这个直接映射,不会做过多的处理(前提没有设置@Table,@Column等属性的时候)。如果有@Column则以@Column为准【对于关联关系的依旧会命名为user_id这种样子,看下图】
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy效果等同于:
  hibernate4中的ImprovedNamingStrategy 表名,字段为小写,当有大写字母的时候会转换为分隔符号“_”。

 

(2)、implicit-strategy的使用:

spring.jpa.hibernate.naming.implicit-strategy 它有如下的值可以配置:

org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImp
org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl

下表说明各个策略的差异:

strategyprimaryTablejoinTablecollectTable
ImplicitNamingStrategyJpaCompliantImplEntity类名Entity物理名称 + 引用Entity的物理名称Entity类名 + 属性名称
ImplicitNamingStrategyLegacyHbmImpl-Entity物理名称 + 属性名称-
ImplicitNamingStrategyLegacyJpaImpl--Entity物理名称 + 属性名称
SpringImplicitNamingStrategy-Entity的物理名称 + 属性名称-

(3)、自定义命名策略的使用:

A、首先,介绍下命名策略的步骤:

    第一步:如果我们没有使用@Table或@Column指定了表或字段的名称,则由SpringImplicitNamingStrategy为我们隐式处理,表名隐式处理为类名,列名隐式处理为字段名。如果指定了表名列名,SpringImplicitNamingStrategy不起作用。
    第二步:将上面处理过的逻辑名称解析成物理名称。无论在实体中是否显示指定表名列名,SpringPhysicalNamingStrategy都会被调用。
​
    所以如果我们想要自定义命名策略,可以根据自己的需求选择继承二者,并在配置文件中通过spring.jpa.hibernate.naming.implicit-strategy 或 spring.jpa.hibernate.naming.physical-strategy 进行指定自己的策略(例如为表名添加指定前缀)。

 

B、其次自定义命名策略,通过继承PhysicalNamingStrategyStandardImpl 或实现PhysicalNamingStrategy 接口:

package com.yozheng.springboot.demo.common;
 
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
/**
 * 自定义数据库映射名称策略
 */
 
@Component
public class DataBaseNamingStrategy implements PhysicalNamingStrategy {
 
    @Value("${database.prefix}")
    private String prefix;  //读取application.properties配置的前缀
 
 
    protected String addUnderscores(String name) {
        if (name == null) {
            return null;
        }
        StringBuffer stringBuffer = new StringBuffer(name.replace('.', '_'));
        for (int i = 1; i < stringBuffer.length() - 1; i++) {
            if (Character.isLowerCase(stringBuffer.charAt(i-1)) && Character.isUpperCase(stringBuffer.charAt(i)) && Character.isLowerCase(stringBuffer.charAt(i+1))) {
                stringBuffer.insert(i++, '_');
            }
        }
        return stringBuffer.toString().toLowerCase();
    }
 
    @Override
    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }
 
    @Override
    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }
 
    @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return Identifier.toIdentifier(prefix + addUnderscores(identifier.getText()));
    }
 
    @Override
    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }
 
    @Override
    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return Identifier.toIdentifier(addUnderscores(identifier.getText()));
    }
 
 
}

 

只需要实现上面:toPhysicalTableName(这个是映射表名), toPhysicalColumnName(映射字段名称)

c、 在application.properties文件配置信息

​
#配置自定义命名策略
spring.jpa.hibernate.naming.physical-strategy=com.example.demo.DataBaseNamingStrategy
#数据库表名前缀
database.prefix=tb_

 

运行测试效果:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w风雨无阻w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值