本文主要讲解 springData Jpa
入门相关知识, 了解JPA规范与Jpa的实现,搭建springboot+dpringdata jpa环境实现基础增删改操做,适合新手学习,老鸟绕道~java
ORM(Object-Relational Mapping)顾名思义就是表示对象关系映射。在面向对象的软件开发中,咱们确定是须要和数据库进行交互的,那么这里就存在一个问题如何将数据库中的表与咱们代码中的对象映射起来尼,咱们只要有一套程序可以作到创建对象与数据库的关联,操做对象就能够直接操做数据库数据,就能够说这套程序实现了ORM对象关系映射mysql
简单的说:ORM就是创建实体类和数据库表之间的关系,从而达到操做实体类就至关于操做数据库表的目的。web
目前市面上主流的ORM框架有Hibernate、Mybatis、Spring Data Jpa等,spring
Mybatis
框架是一个半自动的ORM框架,自己并非彻底面向对象的思想,可是得益于sql与代码的解耦,能够更灵活的操做sql与优化sql,可是同时也带有复杂的映射文件,在国内目前仍是很是主流的。JPA
的全称是 Java Persistence API
, 即 Java
持久化 API
,是 SUN
公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。sql
JPA
经过 JDK 5.0
注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。数据库
JPA
是 JCP
组织发布的 Java EE
标准之一,所以任何声称符合 JPA
标准的框架都遵循一样的架构,提供相同的访问 API
,这保证了基于 JPA
开发的企业应用可以通过少许的修改就可以在不一样的 JPA
框架下运行。编程
JPA
框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的做用。设计模式
简单方便springboot
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下建立实体和建立Java 类同样简单,没有任何的约束和限制,只须要使用 javax.persistence.Entity
进行注释, JPA
的框架和接口也都很是简单,没有太多特别的规则和设计模式的要求,开发者能够很容易的掌握。JPA基于非侵入式原则设计,所以能够很容易的和其它框架或者容器集成微信
查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的天然语法构造查询语句,能够当作是 Hibernate HQL
的等价物。 JPA
定义了独特的 JPQL(Java Persistence Query Language)
, JPQL
是 EJB QL
的一种扩展,它是针对实体的一种查询语言,操做对象是实体,而不是关系数据库的表,并且可以支持批量更新和修改、 JOIN、GROUP BY、HAVING
等一般只有 SQL
才可以提供的高级查询特性,甚至还可以支持子查询。
高级特性
JPA
中可以支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持可以让开发者最大限度的使用面向对象的模型设计企业应用,而不须要自行处理这些特性在关系数据库的持久化。
Spring Data JPA
是 Spring
基于 ORM
框架、 JPA
规范的基础上封装的一套 JPA
应用框架,可以使开发者用极简的代码便可实现对数据库的访问和操做。它提供了包括增删改查等在内的经常使用功能,且易于扩展!学习并使用 Spring Data JPA
能够极大提升开发效率!
Spring Data JPA
让咱们解脱了 DAO
层的操做,基本上全部 CRUD
均可以依赖于它来实现, 在实际的工做工程中,推荐使用 Spring Data JPA + ORM
(如: hibernate
)完成操做,这样在切换不一样的ORM框架时提供了极大的方便,同时也使数据库层操做更加简单,方便解耦JPA
是一套规范,内部是有接口和抽象类组成的。
hibernate
是一套成熟的ORM框架,并且 Hibernate
实现了 JPA
规范,因此也能够称 hibernate
为 JPA
的一种实现方式,咱们使用 JPA
的 API
编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA
是 Spring
提供的一套对JPA操做更加高级的封装,是在 JPA
规范下的专门用来进行数据持久化的解决方案。
springboot
脚手架构建初始环境,咱们不要 web
模块,只须要 spring data jpa
、 mysql
便可spingboot
的强大在这里体现了,为咱们快速构建了开箱即用的环境,咱们在 application.yml
文件中添加咱们须要的配置信息spring: datasource: url: jdbc:mysql:///db?serverTimezone=GMT username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 数据库没有表时自动构建, database: mysql # 指定数据库类型 generate-ddl: true # 自动生成 show-sql: true # 现实sql到控制台 database-platform: org.hibernate.dialect.MySQL8Dialect # 数据库方言 DataBbase枚举内获取
DeptEntity
, 这里用来 lombok
插件省去了 get/set
方法,对于 @Column
注解是能够放在 get
方法上的@Entity @Table(name = "dept", schema = "db", catalog = "") @ToString @Data public class DeptEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "DEPTNO", nullable = false) private int deptno; // name : 数据库中的列名 nullable : 是否为空 length:长度 @Column(name = "DNAME", nullable = true, length = 14) private String dname; @Column(name = "LOC", nullable = true, length = 13) private String loc; @Column(name = "flag", nullable = true) private Integer flag; @Column(name = "type", nullable = true, length = 20) private String type; }
Dao
层接口,须要继承 JPA
的接口 JpaRepository
, 代码以下:public interface DeptRepository extends JpaRepository<DeptEntity,Integer> { }
咱们的 dao 层是一个接口,没有具体的实现,在继承的接口中已经定义了一些经常使用的方法供咱们使用,后面详细介绍
@SpringBootTest public class SpringJpaTest { @Autowired private DeptRepository deptRepository; @Test public void jpaTest(){ List<DeptEntity> list = deptRepository.findAll(); System.out.println(list); } }
Hibernate: select deptentity0_.deptno as deptno1_0_, deptentity0_.dname as dname2_0_, deptentity0_.flag as flag3_0_, deptentity0_.loc as loc4_0_, deptentity0_.type as type5_0_ from dept deptentity0_ [DeptEntity(deptno=10, dname=张三, loc=NEW YORK, flag=10, type=真实类型), DeptEntity(deptno=20, dname=RESEARCH, loc=DALLAS, flag=20, type=11), DeptEntity(deptno=50, dname=zonghebu, loc=CHICAGO, flag=null, type=null), DeptEntity(deptno=60, dname=??, loc=123, flag=null, type=22), DeptEntity(deptno=61, dname=??, loc=123, flag=null, type=null), DeptEntity(deptno=62, dname=??, loc=123, flag=null, type=null), DeptEntity(deptno=72, dname=??, loc=123, flag=null, type=null)]
同时能够看到sql的语句,这是
Hibernate
帮咱们生成的,总的来讲Spring Data Jpa
的实现是依赖了Hibernate
对Jpa
规范的实现,
@Test public void query(){ Sort deptno = Sort.by(Sort.Direction.DESC,"deptno"); List<DeptEntity> all = deptRepository.findAll(deptno); System.out.println(all); } @Test public void insert(){ DeptEntity entity = new DeptEntity(); entity.setDname("质量控制部门"); entity.setFlag(1); entity.setType("test"); //保存同时将保存结果返回 DeptEntity save = deptRepository.save(entity); System.out.println(save); }
增长方法打印:
Hibernate: insert into dept (dname, flag, loc, type) values (?, ?, ?, ?)
DeptEntity(deptno=73, dname=质量控制部门, loc=null, flag=1, type=test)
其余方法再也不赘述都基本类似,这些方法都是继承自 JpaRepository
接口中
在父接口中定义了一套经常使用的查询, 相对比较简单,以下:
Optional<T> findById(ID id); Iterable<T> findAllById(Iterable<ID> ids); List<T> findAll(Sort sort); List<T> findAllById(Iterable<ID> ids);
jpql : jpa query language (jpq查询语言)
特色:
语法或关键字和sql语句相似
查询的是类和类中的属性
须要将JPQL语句配置到接口方法上
1.特有的查询:须要在dao接口上配置方法
2.在新添加的方法上,使用注解的形式配置jpql查询语句
3.注解 : @Query
代码实现以下:
在dao接口中自定义方法并添加注解
/** * 经过 deptno查询 * 使用@Query注解来编写jpql 语句 是面向对象的操做 * sql: select * from dept where deptno = ? * jpql: select * from DeptEntity where deptno = ?1 * * 在jpql中占位符好指定索引,与参数列表对应,从1开始 * @param id * @return */ @Query(value=" from DeptEntity where deptno = ?1") DeptEntity queryDept(Integer id); /** * DML 操做, 须要加@Modifying 注解 * 在多个条件时要注意占位符下标的数字要和参数列表对应 * 须要注意,DML 语句须要事务配置,须要加 @Transactional 注解,通常在业务层,而再也不数据层, * @param name * @param id */ @Query(value="update DeptEntity set dname=?1 where deptno=?2") @Modifying void updateName(String name,Integer id); // JAVA
测试方法代码:
@Test public void queryDept(){ DeptEntity entity = deptRepository.queryDept(10); System.out.println(entity); } /** * 这里须要加 @Transactional 注解来管理事务 */ @Test @Transactional public void updateName(){ deptRepository.updateName("测试",10); }
特有的查询:须要在dao接口上配置方法
在新添加的方法上,使用注解的形式配置sql查询语句
注解 :
@Query
value :jpql语句 | sql语句 nativeQuery :false(使用jpql查询) | true(使用本地查询:sql查询) 是否使用本地查询
示例代码:
dao接口代码:
/** * 须要添加 nativeQuery 参数来设置是都是sql查询, 默认是false ,是jpql查询 * @param num * @return */ @Query(value="select * from dept where flag = ?",nativeQuery=true) List<DeptEntity> queryList(Integer num);
测试代码:
@Test public void nativeQuery(){ List<DeptEntity> list = deptRepository.queryList(10); System.out.println(list); }
是对jpql查询,更加深刻一层的封装
咱们只须要按照SpringDataJpa提供的方法名称规则定义方法,不须要再配置jpql语句,完成查询
规则:
findBy开头: 表明查询
对象中属性名称(首字母大写)
含义:根据属性名称进行查询
接口代码:
/** * 方法名的约定: * findBy : 查询 * 对象中的属性名(首字母大写) : 查询的条件 * Dname * * 默认状况 : 使用 等于的方式查询 * 特殊的查询方式 * * findAllByDname -- 根据名称查询 * * 再springdataJpa的运行阶段 * 会根据方法名称进行解析 findBy from xxx(实体类) * 属性名称 where dname = * * 1.findBy + 属性名称 (根据属性名称进行完成匹配的查询=) * 2.findBy + 属性名称 + “查询方式(Like | isnull)” * findAllByDnameLike * 3.多条件查询 * findBy + 属性名 + “查询方式” + “多条件的链接符(and|or)” + 属性名 + “查询方式” */ List<DeptEntity> findAllByDname(String name); List<DeptEntity> findAllByDnameAndAndFlag(String name,Integer num); List<DeptEntity> findAllByDnameLike(String name);
测试代码省略
这三个方法乍一看都是同样的功能,都是经过主键返回一个实体,可是再实际使用中仍是有区别的,尤为在一对一的关系中咱们若是直接用 getOne
查询,会出现 下面错误:
org.hibernate. LazyInitializationException: could not initialize proxy - no Session
下面咱们简单说一下这三个方法:
getOne是懒加载,须要在 springboot
增长这个配置: spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
,yml中也是同样配置。但这种方式不太友好,建议不要使用。
每次初始化一个实体的关联就会建立一个临时的 session
来加载,每一个临时的 session
都会获取一个临时的数据库链接,开启一个新的事物。这就致使对底层链接池压力很大,并且事物日志也会被每次 flush
.
设想一下:假如咱们查询了一个分页
list
每次查出1000条,这个实体有三个lazy
关联对象, 那么,恭喜你,你至少须要建立3000
个临时session+connection+transaction
.
getOne
来自 JpaReposiroty
接口,对于传入的标识则返回一个实体的引用;且取决于该方法的实现,可能会出现 EntityNotFoundException
,并会拒绝一些无效的标识;
findById
来自 CrudRepository
接口,经过它的 id
返回一个实体;
findById返回一个Optional对象;
findOne
来自 QueryByExampleExecutor
接口, 返回一个经过 Example
匹配的实体或者 null
;
findOne返回一个Optional对象,能够实现动态查询
本文由AnonyStar 发布,可转载但需声明原文出处。
仰慕「优雅编码的艺术」 坚信熟能生巧,努力改变人生
欢迎关注微信公帐号 :coder简码 获取更多优质文章
更多文章关注笔者博客 :IT简码