源码地址
springboot2教程系列
Spring Data JPA是Spring Data的子模块。使用Spring Data,使得基于“repositories”概念的JPA实现更简单和容易。Spring Data JPA的目标是大大简化数据访问层代码的编码。作为使用者,我们只需要编写自己的repository接口,接口中包含一些个性化的查询方法,Spring Data JPA将自动实现查询方法。
JPA默认使用hibernate作为ORM实现,所以,一般使用Spring Data JPA即会使用hibernate。我们再看看hibernate的官方概念,Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
这样看,Spring Data JPA与MyBatis对比,起始也就是hibernate与MyBatis对比。所以,我们直接来比较后两者。
从基本概念和框架目标上看,两个框架差别还是很大的。hibernate是一个自动化更强、更高级的框架,毕竟在java代码层面上,省去了绝大部分sql编写,取而代之的是用面向对象的方式操作关系型数据库的数据。而MyBatis则是一个能够灵活编写sql语句,并将sql的入参和查询结果映射成POJOs的一个持久层框架。所以,从表面上看,hibernate能方便、自动化更强,而MyBatis 在Sql语句编写方面则更灵活自由。
但这只是从使用层面上看两者的区别,并未涉及的本质。但如果看问题,值看浅层次、表象问题的话,就不能理解技术本质,也不能发挥技术的最多效用。所以,如果更上一个抽象层次去看,对于数据的操作,hibernate是面向对象的,而MyBatis是面向关系的。当然,用hibernate也可以写出面向关系代码和系统,但却得不到面向关系的各种好处,最大的便是编写sql的灵活性,同时也失去面向对象意义和好处——一句话,不伦不类。那么,面向对象和关系型模型有什么不同,体现在哪里呢?实际上两者要面对的领域和要解决的问题是根本不同的:面向对象致力于解决计算机逻辑问题,而关系模型致力于解决数据的高效存取问题。我们不妨对比一下面向对象的概念原则和关系型数据库的不同之处:
从上面两者基本概念和思想的对比来看,可以得出结论hibernate和MyBatis两个框架的侧重点完全不同。所以我们就两个框架选择上,就需要根据不同的项目需求选择不同的框架。在框架的使用中,也要考虑考虑框架的优势和劣势,扬长避短,发挥出框架的最大效用,才能真正的提高项目研发效率、完成项目的目标。但相反,如果使用Spring Data JPA和hibernate等ORM的框架而没有以面向对象思想和方法去分析和设计系统,而是抱怨框架不能灵活操作sql查询数据,那就是想让狗去帮你拿耗子了。
那么,话题再说回来,使用两个框架时候的时候,也要注意最佳的步骤和流程。下面我们来分别讨论一下,hibernate的一般使用步骤如下:
完成以上步骤,基本上完成了体统中主要的业务概念类和表结构的设计工作,只是完成表结构设计的出发点事如何持久化系统的对象,同时兼顾数据库表、字段、字段类型、表的关联关系的合理性和合规性,而不是单纯表设计。这两者思考和关注点还是有很大差别的。另外,需要说明一点,这只是使用hibernate的最通用步骤,实际操作过程中还是需要根据具体项目情况来安排。
而MyBatis对于面向对象的概念强调比较少,更适用于灵活的对数据进行增、删、改、查,所以在系统分析和设计过程中,要最大的发挥MyBatis的效用的话,一般使用步骤则与hibernate有所区别:
这样看来MyBatis更适合于面向关系(或面向数据、或面向过程)的系统设计方法,这样的系统一般称为“事务脚步”系统(事务脚步(Transaction Script) 出自Martin Fowler 2004年所著的企业应用架构模式(Patterns of Enterprise Application Architecture))。而hibernate(也可以说Spring Data JPA)更适合于构建领域模型类的系统。当然,我们也不能说MyBatis无法构建领域模型驱动的系统,而hibernate无法构建事务脚步系统。只是用MyBatis构建领域模型要做更多、更脏、更累的工作;而用hibernate构建一个事务脚本系统有些大材小用,数据的查询反而没那么灵活。
综合上面所有描述和对比,我们对这两个框架的本质区别应该有所了解了。我们了解了这些区别,可以帮助我们选择更合适的框架,同时,也可以利用不同的框架,让他们去做更合适事,这也是所谓的物尽其用吧,更不至于我们“为物所役”。
org.springframework.boot
spring-boot-starter-data-jpa
spring:
messages:
basename: i18n/Messages,i18n/Pages
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置当前要使用的数据源的操作类型
driver-class-name: org.gjt.mm.mysql.Driver # 配置MySQL的驱动程序类
url: jdbc:mysql://47.106.106.53:3306/mybatis?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 # 数据库连接地址
username: root # 数据库用户名
password: Rojao@123
# JPA 相关配置
spring.jpa.database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database: mysql
# 显示后台处理的SQL语句
spring.jpa.show-sql: true
# 自动检查实体和数据库表是否一致,如果不一致则会进行更新数据库表
spring.jpa.hibernate.ddl-auto: create
spring.jpa.show-sql=true 配置在日志中打印出执行的 SQL 语句信息。
spring.jpa.hibernate.ddl-auto=create 配置指明在程序启动的时候要删除并且创建实体类对应的表。这个参数很危险,因为他会把对应的表删除掉然后重建。所以千万不要在生成环境中使用。只有在测试环境中,一开始初始化数据库结构的时候才能使用一次。
create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因(一般只会在第一次创建时使用)
create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除
update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会
validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 。在 SrpingBoot 2.0 版本中,Hibernate 创建数据表的时候,默认的数据库存储引擎选择的是 MyISAM (之前好像是 InnoDB,这点比较诡异)。这个参数是在建表的时候,将默认的存储引擎切换为 InnoDB 用的。
@Data
@Entity
@Table(name = "bus_receiver")
public class BusReceiverEntity {
//主键
@Id
@Column(name="id")
private Long id;
//姓名
@Column(name="name",length = 32)
private String name;
//区域
@Column(name="region_code")
private String regionCode;
//地址
@Column(name="address")
private String address;
//地址英文名字
@Column(name="enname")
private String enName;
//家庭成员
@Column(name="memberfamily")
private int memberFamily;
//创建时间
@Column(name="create_date")
private Date createDate;
}
其中:
* 常用注解解释:
* @Entity 用来标示这个类是实体类
* @Table 实体类与数据表的映射,通过name确定在表名(默认类名为表名)
* @Id 主键注解,表示该字段为主键
* @GeneratedValue 定义主键规则,默认AUTO
* @Column 类属性与表字段映射注解,其中可以设置在字段名,长度等信息
* @ManyToOne 多对一,可以设置数据加载方式等 默认加载方式是EAGER 就是使用left join
* @OneToMany 一对多 默认加载方式是 LAZY 懒加载
* @JoinColumn 与*对*配合使用,用来设置外键名等信息
* @Basic 实体类中会默认为每一个属性加上这个注解,表示与数据表存在关联,
* 没有使用Column注解的类属性会以属性名作为字段名,驼峰命名需要转为_
* @Temporal 对于Date属性的格式化注解,有 TIME,DATE,TIMESTAMP 几个选择
* @Transient 若存在不想与数据表映射的属性,则需要加上该注解
在 Spring Data JPA 的世界里,实现一个持久层的服务是一个非常简单的事情。以上面的 UserDO 实体对象为例,我们要实现一个增加、删除、修改、查询功能的持久层服务,那么我只需要声明一个接口,这个接口继承
org.springframework.data.repository.Repository
然后再简单的在这个接口上增加一个 @Repository 注解就结束了。
@Repository
public interface BusReceiverDao extends JpaRepository<BusReceiverEntity,Long> {
}
已经拥有下下面的功能
Dao 保存实体功能
Dao 保存实体删除功能
Dao 查询实体功能
该Dao成继承了JpaRepository接口,指定了需要操作的实体对象和实体对象的主键类型,通过查看JpaRepository接口源码可以看到,里面已经封装了创建(save)、更新(save)、删除(delete)、查询(findAll、findOne)等基本操作的函数,使用起来非常方便了,但是还是会存在一些复杂的sql,spring-data-jpa还提供了一个非常方便的方式,通过实体属性来命名方法,它会根据命名来创建sql查询相关数据,对应更加复杂的语句,还可以用直接写sql来完成,具体例子如上所示。
如果想要根据实体的某个属性
来进行查询我们可以在 Dao 接口中进行接口声明。例如,如果我们想根据实体的 name 这个属性来进行查询,只需要
BusReceiverEntity findByName(String name);
这种方式非常强大,不经能够支持单个属性,还能支持多个属性组合
。例如如果我们想查找账号和密码同时满足查询条件的接口。那么我们在 Dao 接口中声明
BusReceiverEntity findByNameAndAddress(String name,String address);
这个语句结构可以用下面的表来说明
如果上述的情况还无法满足需要。那么我们就可以通过通过 import org.springframework.data.jpa.repository.Query
注解来解决这个问题。
@Query("SELECT r FROM BusReceiverEntity r where r.name = ?1")
BusReceiverEntity findByName(String name);*/
这里是用 PQL 的语法来定义一个查询。 (注意是HQL,不是SQL语句)
如果你习惯编写 SQL 语句来完成查询,还可以在用下面的方式实现
@Query(nativeQuery = true, value = "SELECT * FROM AUTH_USER WHERE name = :name1 OR name = :name2 ")
List<UserDO> findSQL(@Param("name1") String name1, @Param("name2") String name2);
这里在 @Query 注解中增加一个 nativeQuery = true 的属性,就可以采用原生 SQL 语句的方式来编写查询。
(
@Param
注解注入参数,:
引入参数,需要指定参数名)
@Query
和@Modifying
同时使用用来自定义数据更改操作的SQL,@Transactional
负责提交事务
@Transactional
@Query(value = "update BusReceiverEntity p set p.name = ?1")
@Modifying
Integer updateName(String name);
org.springframework.boot
spring-boot-starter-cache
org.hibernate
hibernate-jcache
5.4.3.Final
org.ehcache
ehcache
3.5.2
此处需要注意,如果高版本的 SpringBoot,我们需要使用org.hibernate:hibernate-jcache,而不是org.hibernate:hibernate-ehcache。否则启动异常
这里需要注意的是,百度里面的文章大部分都是告诉大家怎么配置ehcache2.x,但是ehcache的2.x和3.x完全就两码事情了 ,这里使用ehcache3.x配置
600
2000
100
1
24
#开启二级缓存
spring.jpa.properties.hibernate.cache.use_second_level_cache: true
spring.jpa.properties.cache.use_query_cache: true
spring.jpa.properties.cache.provider_configuration_file_resource_path: ehcache.xml
#指定缓存provider
spring.jpa.properties.hibernate.cache.region.factory_class: org.hibernate.cache.jcache.internal.JCacheRegionFactory
spring.jpa.open-in-view: false
spring.jpa.properties.javax.persistence.sharedCache.mode: ENABLE_SELECTIVE
@SpringBootApplication
@EnableCaching
public class JpaApplication {
public static void main(String[] args){
SpringApplication app = new SpringApplication(JpaApplication.class);
// app.setWebEnvironment(false);
app.run(args);
}
}
@Entity
@Table(name = "bus_receiver")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class BusReceiverEntity {
....
}
在使用 jpa 的 findById
方法进行查询和 save
进行更新数据的时候,hibernate 就会使用到缓存了 .
根据属性来查询
是不使用缓存