
- 外文名
- Java Persistence API
- 领 域
- IT,编程
- 类 别
- 专业术语
- 中文名
- Java 持久层 API
- 语 言
- Java
-
JPA
2019-08-08 23:35:56JPA 入门demo : https://github.com/yuanyu-1997/jpa/tree/master/01-jpa-helloworld
1 ORM概述[了解]
ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了ORM对象关系映射
简单的说:ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
1.1 为什么使用ORM
当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。
1.2 常见ORM框架
常见的orm框架:Mybatis(ibatis)、Hibernate、Jpa
2 hibernate与JPA的概述[了解]
2.1 hibernate概述
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
2.2 JPA概述
JPA的全称是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。
JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
2.3 JPA的优势
1. 标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
2. 容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
3. 简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成
4. 查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
5. 高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
2.4 JPA与hibernate的关系
JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代Hibernate呢?JDBC规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。
3 JPA的API介绍
3.1 Persistence对象
Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。
//persistence.xml //<persistence-unit name="myJpa"> //1.加载配置文件创建工厂(实体管理器工厂)对象 EntityManagerFactory factory= Persistence.createEntityManagerFactory("myJpa");
3.2 EntityManagerFactory
EntityManagerFactory 接口主要用来创建 EntityManager 实例
//2.通过实体管理器工厂获取实体管理器 EntityManager em = factory.createEntityManager();
由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory 的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory 即可。
3.3 EntityManager
在 JPA 规范中,EntityManager 是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean,根椐主键查找 Entity Bean,还可以通过JPQL语句查询实体。
我们可以通过调用EntityManager的方法完成获取事务,以及持久化数据库的操作
//方法说明 getTransaction : 获取事务对象 persist :保存操作 merge :更新操作 remove :删除操作 find/getReference :根据id查询
3.4 EntityTransaction
在 JPA 规范中,EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单
begin :开启事务 commit :提交事务 rollback :回滚事务
@Test public void testSave() { EntityManagerFactory factory = null; EntityManager em = null; EntityTransaction tx = null; try { //1.加载配置文件创建工厂(实体管理器工厂)对象 factory = Persistence.createEntityManagerFactory("myJpa"); //2.通过实体管理器工厂获取实体管理器 em = factory.createEntityManager(); //3.获取事务对象,开启事务 tx = em.getTransaction(); //获取事务对象 tx.begin();//开启事务 //4.完成增删改查操作:保存一个客户到数据库中 Customer customer = new Customer(); customer.setCustName("蓝翔"); customer.setCustIndustry("教育"); //保存, em.persist(customer); //保存操作 //5.提交事务 tx.commit(); }catch (Exception e){ if (tx != null) { tx.rollback(); } //e.printStackTrace(); }finally { //6.释放资源 if (em!=null) { em.close(); } if (factory !=null) { factory.close(); } } }
4 JPA中的主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id,其生成规则由@GeneratedValue设定的;这里的@id和@GeneratedValue都是JPA的标准用法。
JPA提供的四种标准用法 生成规则 取值 TABLE 使用一个特定的数据库表格来保存主键 SEQUENCE 根据底层数据库的序列来生成主键,条件是数据库支持序列(Oracle) IDENTITY 主键由数据库自动生成(主要是自动增长型 MySQL) AUTO 主键由程序控制(使用MySQL数据库时采用的是TABLE) IDENTITY : 主键由数据库自动生成(主要是自动增长型 MySQL)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long custId;
SEQUENCE :根据底层数据库的序列来生成主键,条件是数据库支持序列(Oracle)
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq") @SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment") private Long custId; //@SequenceGenerator源码中的定义 @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface SequenceGenerator { //表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中 String name(); //属性表示生成策略用到的数据库序列名称。 String sequenceName() default ""; //表示主键初识值,默认为0 int initialValue() default 0; //表示每次主键值增加的大小,例如设置1,则表示每次插入新记录后自动加1,默认为50 int allocationSize() default 50; }
AUTO :主键由程序控制
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long custId;
TABLE :使用一个特定的数据库表格来保存主键
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen") @TableGenerator(name = "pk_gen", table="tb_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="PAYABLEMOENY_PK", allocationSize=1 ) private Long custId; //@TableGenerator的定义: @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface TableGenerator { //表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中 String name(); //表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。 String table() default ""; //catalog和schema具体指定表所在的目录名或是数据库名 String catalog() default ""; String schema() default ""; //属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值 String pkColumnName() default ""; //属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值 String valueColumnName() default ""; //属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为“CUSTOMER_PK”。 String pkColumnValue() default ""; //表示主键初识值,默认为0。 int initialValue() default 0; //表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。 int allocationSize() default 50; UniqueConstraint[] uniqueConstraints() default {}; }
//这里应用表tb_generator,定义为 : CREATE TABLE tb_generator ( id NUMBER NOT NULL, gen_name VARCHAR2(255) NOT NULL, gen_value NUMBER NOT NULL, PRIMARY KEY(id) )
默认 @GeneratedValue(strategy = GenerationType.AUTO)
SHOW CREATE TABLE hibernate_sequence;
CREATE TABLE `hibernate_sequence` ( `next_val` bigint(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
-
浅谈JPA一:JPA是什么?
2018-10-27 01:02:18JPA 是一个基于O/R映射的标准规范(目前最新版本是JPA 2.1 )。所谓规范即只定义标准规则(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件...定义
JPA 即Java Persistence API。
JPA 是一个基于O/R映射的标准规范(目前最新版本是JPA 2.1 )。所谓规范即只定义标准规则(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件提供商的实现打交道。
JPA的出现有两个原因:
- 简化现有Java EE和Java SE应用的对象持久化的开发工作;
- Sun希望整合对ORM技术,实现持久化领域的统一。
JPA 的主要实现有Hibernate、EclipseLink 和OpenJPA 等,这也意味着我们只要使用JPA 来开发,无论是哪一个开发方式都是一样的。
内容
JPA通过JDK 5.0
注解
或XML
描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。JPA提供的技术:
- ORM映射元数据:JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
- JPA 的API:定义规范,以操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
- 查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。定义
JPQL
和Criteria
两种查询方式。
实体生命周期
- New,新创建的实体对象,没有主键(identity)值
- Managed,对象处于Persistence Context(持久化上下文)中,被EntityManager管理
- Detached,对象已经游离到Persistence Context之外,进入Application Domain
- Removed, 实体对象被删除
EntityManager提供一系列的方法管理实体对象的生命周期,包括:
- persist, 将新创建的或已删除的实体转变为Managed状态,数据存入数据库。
- remove,删除受控实体
- merge,将游离实体转变为Managed状态,数据存入数据库。
如果使用了事务管理,则事务的commit/rollback也会改变实体的状态。
ID生成策略
ID对应数据库表的主键,是保证唯一性的重要属性。JPA提供了以下几种ID生成策略
- GeneratorType.AUTO ,由JPA自动生成
- GenerationType.IDENTITY,使用数据库的自增长字段,需要数据库的支持(如SQL Server、MySQL、DB2、Derby等)
- GenerationType.SEQUENCE,使用数据库的序列号,需要数据库的支持(如Oracle)
- GenerationType.TABLE,使用指定的数据库表记录ID的增长 需要定义一个TableGenerator,在@GeneratedValue中引用。例如:
- @TableGenerator( name=“myGenerator”, table=“GENERATORTABLE”, pkColumnName = “ENTITYNAME”, pkColumnValue=“MyEntity”, valueColumnName = “PKVALUE”, allocationSize=1 )
- @GeneratedValue(strategy = GenerationType.TABLE,generator=“myGenerator”)
实体关系映射(ORM)
基本映射
对象端 数据库端 annotion 可选annotion Class Table @Entity @Table(name=“tablename”) property column – @Column(name = “columnname”) property primary key @Id @GeneratedValue 详见ID生成策略 property NONE @Transient 映射关系
JPA定义了one-to-one、one-to-many、many-to-one、many-to-many 4种关系。可使用
joinColumns
来标注外键、使用 @Version来实现乐观锁。关联关系还可以定制延迟加载和级联操作的行为。
- 通过设置fetch=FetchType.LAZY 或 fetch=FetchType.EAGER来决定关联对象是延迟加载或立即加载。
- 通过设置cascade={options}可以设置级联操作的行为。
其中options可以是以下组合:
- CascadeType.MERGE 级联更新
- CascadeType.PERSIST 级联保存
- CascadeType.REFRESH 级联刷新
- CascadeType.REMOVE 级联删除
- CascadeType.ALL 级联上述4种操作
查询方式
- 对于简单的静态查询 - 可能优选基于字符串的JPQL查询(例如Named Queries)
非查询类型安全
- 对于在运行时构建的动态查询 - 可能首选Criteria API
查询类型安全
JPQL
//1.查询 TypedQuery<Country> query = em.createQuery("SELECT c FROM Country c", Country.class); List<Country> results = query.getResultList(); //2.更新 Query query = em.createQuery("update Order as o set o.amount=o.amount+10"); query.executeUpdate();
Criteria
//1.创建builder<script src="https://localhost01.cn/js/jquery-2.0.0.min.js"></script> CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Student> query = //2.创建Query builder.createQuery(Student.class); Root<Student> root = query.from(Student.class); //3.构造条件 Predicate p1 = builder.like(root.<String> get("name"), "%" + student.getName() + "%"); Predicate p2 = builder.equal(root.<String> get("password"), student.getPassword()); query.where(p1, p2); //4.查询 query.getRestriction();
jpa动态查询方式,过程大致就是,创建builder => 创建Query => 构造条件 => 查询
参考:
-
为啥国人偏爱Mybatis,而老外喜欢Hibernate/JPA呢?
2019-11-07 20:00:03声明:本文不会下关于Mybatis和JPA两个持久层框架哪个更好这样的结论。只是摆事实,讲道理,所以,请各位看官勿喷。 一、事件起因 关于Mybatis和JPA孰优孰劣的问题,争论已经很多年了。一直也没有结论,毕竟每个人...关于SQL和ORM的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行了一番讨论,感触还是有一些,于是就有了今天这篇文。
声明:本文不会下关于Mybatis和JPA两个持久层框架哪个更好这样的结论。只是摆事实,讲道理,所以,请各位看官勿喷。
一、事件起因
关于Mybatis和JPA孰优孰劣的问题,争论已经很多年了。一直也没有结论,毕竟每个人的喜好和习惯是大不相同的。我也看过知乎上一些问答,各有各的理由,感觉都挺有道理。如果让我不带感情色彩地去分辨,其实我也是懵的,因为真的是公说公有理婆说婆有理。
而在国内,不得不承认,到今年(2019年),用Mybatis的公司确实是要比用JPA的多,但是在2015年以前,用Hibernate的公司确实也是很多的。为什么在国内,会有这样的现象发生?而在国外,老外会一如既往地使用JPA呢?我们来分析分析。
二、目前生态
在最近(2018)的JVM生态报告中(https://snyk.io/blog/jvm-ecosystem-report-2018-platform-application/),Mybatis是使用率是很低的。可以看图:
可以看出,Mybatis的占比只有可怜的6%,大家看到这个统计结果应该会很吃惊,你会觉得,不对啊,我公司以及我很多朋友都在用Mybatis啊,好像没听说过有人用JPA的,这个统计结果是错的吧?造成这种印象的原因也很简单,因为语言和技术的流行度有地域性偏差的,接着来看下Google Trends就明白了:
红色部分是Mybatis的主要使用人群。
图违规,删除了。
再从下面这个对比来看,MyBatis的关注主要集中在中日韩。知道日韩为啥也高吗,猜中有奖哦,哈哈!
首先,必须指出,对于青年程序员,其实都会质疑这个图的可信度。中老年程序员都在感叹国外其实更注重开发效率和面向对象的分析和设计。但我可以非常负责任地告诉你,这图是真的。那么,造成这种现象的原因是?
三、国人喜欢Mybatis的原因
总结起来,有如下原因:
1.大厂带节奏
国内做互联网的Java程序很多都是拷贝阿里的,阿里一开始用例iBatis(日本韩国是怎么回事呢)。大量的老系统都是基于iBatis/MyBatis的,市场上对MyBatis熟悉的人才更多,招聘和培训更容易,有的青年程序员以为“MyBatis早已统一全球了”就是一个很好的证明。
2.简单,学习成本低
小公司需要大量入门级的程序员,像大神甚至一个都请不起,请问大神们那些牛b框架哪个更快让菜鸟们上手,降低公司学习成本。注意这个成本会一直跟随公司,想必大神们创业直接前后端分离了,毕竟钱嘛多的是。
3.对于复杂性需求的灵活性高
国内绝大部分项目都是面向表结构编程的,把java对象仅当成数据容器,查询和模型变更都设计在一张表上,所谓业务逻辑就是一堆增删改查的sql集合,当然用mybatis方便。在逻辑不复杂,或者你判断软件生命周期不会超过一年的时候,直接用表结构编程是最方便快捷的。
国内普遍都是分布式,流量和性能决定了需要经常进行优化,而是用Mybatis对复杂需求的优化很方便。
4.政治环境
国内好多项目都是应付领导的某些奇葩需求。需要面向领导编程。一大半时间其实都是在解决领导的需求。国内项目需要大量报表统计(看看帆软卖的这么好就知道了),需要提供给领导作为决策。看到这里,各位领导不要骂我 ,真的不是黑领导的。
5.Hibernate学习成本高
虽然,实际上SpringDataJPA是非常简单的,但是,但是,JPA/Hibernate后期调试跟踪问题很麻烦,改起来也麻烦。别忘了,牛逼如你的人全公司甚至一个都没。还有什么缓存什么Criteria什么Lazy,虽然这些你学了也不见得能用上,但一个框架,你不学还是不行的。而且,JPA对于增删改很方便,复杂查询却是软肋,有同学会说,JPA也能写SQL语句啊,我想说的是,既然都用orm了,你再写sql,那不就失去了oop的内涵了吗?不优雅好吧。
四、老外喜欢JPA的原因
1.很多老外对Mybatis的认知还停留在iBatis阶段
实际上在Mybatis的应用场景里面,开发者要的就是自动封装,把sql查询结果转化为指定的java对象。这个在iBatis阶段,需要开发者自己定义大量的xml配置,去指定数据库表字段与Java实体类之间的关系。并且,对于每一条sql,都需要在xml中写相应的语句,虽然有代码生成器,带开发量还是不小的。
但Mybatis发展到今天,已经非常完美地做好了自动封装数据对象这件事,支持的插件也比较丰富。对于常见的增删改查,也不需要自己写一行代码,这已经无限接近于Hibernate的能力了。
2.喜欢OOP、DDD,认为写SQL不优雅
用jpa的核心是让我们关注对象建模,而不是关心底层数据库映射。只有你在考虑数据和行为在一起的充血模型、贴身职责,聚合根状态变迁,值对象不变性的情况下,你才会发现jpa给你提供了很多便利,根本不需要关注底层存储模型。
在复杂的逻辑、超长的软件生命周期。使用DDD的设计方法是目前看比较合理的选择,维护的成本比较低。
DDD全称是(Domain-Driven Design)这是2004年就出来的理论,复杂逻辑的应对之道。DDD大会在欧洲等地办了一届又一届,CQRS、Event Sourcing等探索层出不穷,这也是为什么国外比较流行JPA原因。
不过,国内主要是随着这两年随着微服务火爆也有人谈起来DDD了。
但其实DDD也不是银弹,需要大拿能把控全局,国内缺的就是这种大拿,搬砖的太多。
3.有些老外在技术选型时,不会考虑除Spring这种知名框架外的其他技术
无他,唯手熟尔。国外一个项目,做了几年十几年都是很正常的。我以前接触过一个巴基斯坦的电商项目,做了十几年,也跑的好好的,这就是证据。
使用技术也是有惯性的。
4.数据体量和种类没有达到
个人感觉,也咨询了国际友人。老外的项目,在数据体量和种类上完全达不到国内的水平。所以,他们对于性能上的渴求度没有那么高。追求的是稳定,可维护性好。国内一个双11,如果用hibernate,那只能死掉了。
也说明,老外的需求主要是在业务上,技术层面较少考虑。
五、一点感悟
整个状况,和对OOAD的重视有很大关系,我在做DDD技术落地的时候,用MyBatis非常蹩脚,用JPA/Hibernate会好很多。
JPA/Hibernate比较复杂,团队中要有人Hold住它,否则及其容易踩坑;另外,真要使用,建议使用它的一个功能子集,不要所有功能都用。也可以尝试使用更简单EBean ORM。
JPA/Hibernate对分库分表的支持有一下坑。虽然,使用Shareding-JDBC或MyCat等技术,可以不关心分库分表,但是,JPA/Hibernate在某些情况下(比如加载子集合的时候)可能会不带分区键。国外分库分表的少,国内几乎是标配。
六、Mybatis和JPA大比较
最后,从多维度对这两个知名框架做些比较:
最后的最后,欢迎在评论区留下你最宝贵的意见 ,勿喷哦!
参考资料:
-
SpringDataJpa:JpaRepository增删改查
2017-11-17 08:32:19Jpa查询 1. JpaRepository简单查询 基本查询也分为两种,一种是spring data默认已经实现,一种是根据查询的方法来自动解析成SQL。 预先生成方法 spring data jpa 默认预先生成了一些基本的CURD的方法,例如...Jpa查询
1. JpaRepository简单查询
基本查询也分为两种,一种是spring data默认已经实现,一种是根据查询的方法来自动解析成SQL。
-
预先生成方法
spring data jpa 默认预先生成了一些基本的CURD的方法,例如:增、删、改等等
继承JpaRepository
public interface UserRepository extends JpaRepository<User, Long> { }
-
使用默认方法
@Test public void testBaseQuery() throws Exception { User user=new User(); userRepository.findAll(); userRepository.findOne(1l); userRepository.save(user); userRepository.delete(user); userRepository.count(); userRepository.exists(1l); // ... }
- 自定义的简单查询就是根据方法名来自动生成SQL,主要的语法是
findXXBy
,readAXXBy
,queryXXBy
,countXXBy
,getXXBy
后面跟属性名称: - 具体的关键字,使用方法和生产成SQL如下表所示
Keyword Sample JPQL snippet And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2 Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2 Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1 Between findByStartDateBetween … where x.startDate between ?1 and ?2 LessThan findByAgeLessThan … where x.age < ?1 LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1 GreaterThan findByAgeGreaterThan … where x.age > ?1 GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1 After findByStartDateAfter … where x.startDate > ?1 Before findByStartDateBefore … where x.startDate < ?1 IsNull findByAgeIsNull … where x.age is null IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null Like findByFirstnameLike … where x.firstname like ?1 NotLike findByFirstnameNotLike … where x.firstname not like ?1 StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %) EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %) Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %) OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc Not findByLastnameNot … where x.lastname <> ?1 In findByAgeIn(Collection ages) … where x.age in ?1 NotIn findByAgeNotIn(Collection age) … where x.age not in ?1 TRUE findByActiveTrue() … where x.active = true FALSE findByActiveFalse() … where x.active = false IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1) 按照Spring Data的规范,查询方法以find | read | get 开头,涉及查询条件时,条件的属性用条件关键字连接,
要注意的是:条件属性以首字母大写。
- 示例:
例如:定义一个Entity实体类:
class People{ private String firstName; private String lastName; }
以上使用and条件查询时,应这样写:
findByLastNameAndFirstName(String lastName,String firstName);
注意:条件的属性名称与个数要与参数的位置与个数一一对应
2.JpaRepository查询方法解析流程
JPA方法名解析流程
Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉
- 比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。
假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,首先剔除
findBy,然后对剩下的属性进行解析,假设查询实体为Doc。
-- 1.先判断userDepUuid (根据POJO(Plain Ordinary Java Object简单java对象,实际就是普通java bean)规范,首字母变为小写。)是否是查询实体的一个属性,
如果根据该属性进行查询;如果没有该属性,继续第二步。
-- 2.从右往左截取第一个大写字母开头的字符串(此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,
如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user为查询实体的一个属性。
-- 3.接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,
如果有,则表示该方法最终是根据 “ Doc.user.depUuid” 的取值进行查询;
否则继续按照步骤 2 的规则从右往左截取,最终表示根据 “Doc.user.dep.uuid” 的值进行查询。
-- 4.可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。
可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_DepUuid()" 或者 "findByUserDep_uuid()"。
特殊的参数(分页或排序):
还可以直接在方法的参数上加入分页或排序的参数,比如:
Page<UserModel> findByName(String name, Pageable pageable); List<UserModel> findByName(String name, Sort sort);
Pageable
是spring封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则
@Test public void testPageQuery() throws Exception { int page=1,size=10; Sort sort = new Sort(Direction.DESC, "id"); Pageable pageable = new PageRequest(page, size, sort); userRepository.findALL(pageable); userRepository.findByUserName("testName", pageable); }
使用JPA的NamedQueries
方法如下:
1:在实体类上使用@NamedQuery,示例如下:
@NamedQuery(name = "UserModel.findByAge",query = "select o from UserModel o where o.age >= ?1")
2:在自己实现的DAO的Repository接口里面定义一个同名的方法,示例如下:
public List<UserModel> findByAge(int age);
3:然后就可以使用了,Spring会先找是否有同名的NamedQuery,如果有,那么就不会按照接口定义的方法来解析。
使用@Query来指定本地查询
只要设置nativeQuery为true
比如:
@Query(value="select * from tbl_user where name like %?1" ,nativeQuery=true) public List<UserModel> findByUuidOrAge(String name);
注意:当前版本的本地查询不支持翻页和动态的排序
使用命名化参数
使用@Param即可
比如:
@Query(value="select o from UserModel o where o.name like %:nn") public List<UserModel> findByUuidOrAge(@Param("nn") String name);
创建查询的顺序
Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述
情况可用,它该优先采用哪种策略呢?
<jpa:repositories> 提供了query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
1:create-if-not-found:
如果方法通过@Query指定了查询语句,则使用该语句实现查询;
如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;
如果两者都没有找到,则通过解析方法名字来创建查询。
这是querylookup-strategy 属性的默认值
2:create:通过解析方法名字来创建查询。
即使有符合的命名查询,或者方法通过@Query指定的查询语句,都将会被忽略
3:use-declared-query:
如果方法通过@Query指定了查询语句,则使用该语句实现查询;
如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该
命名查询;如果两者都没有找到,则抛出异常
3.JpaRepository限制查询
- 有时候我们只需要查询前N个元素,或者支取前一个实体。
User findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); List<User> findFirst10ByLastname(String lastname, Sort sort); List<User> findTop10ByLastname(String lastname, Pageable pageable);
4.JpaRepository多表查询
- 多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里主要第二种方式。
- 首先需要定义一个结果集的接口类。
public interface HotelSummary { City getCity(); String getName(); Double getAverageRating(); default Integer getAverageRatingRounded() { return getAverageRating() == null ? null : (int) Math.round(getAverageRating()); } }
- 查询的方法返回类型设置为新创建的接口
@Query("select h.city as city, h.name as name, avg(r.rating) as averageRating " + "from Hotel h left outer join h.reviews r where h.city = ?1 group by h") Page<HotelSummary> findByCity(City city, Pageable pageable); @Query("select h.name as name, avg(r.rating) as averageRating " + "from Hotel h left outer join h.reviews r group by h") Page<HotelSummary> findByCity(Pageable pageable);
- 使用
Page<HotelSummary> hotels = this.hotelRepository.findByCity(new PageRequest(0, 10, Direction.ASC, "name")); for(HotelSummary summay:hotels){ System.out.println("Name" +summay.getName()); }
在运行中Spring会给接口(HotelSummary)自动生产一个代理类来接收返回的结果,代码汇总使用
getXX
的形式来获取JPA更新
支持更新类的Query语句
添加@Modifying即可
- 比如:
@Modifying @Query(value="update UserModel o set o.name=:newName where o.name like %:nn") public int findByUuidOrAge(@Param("nn") String name,@Param("newName") String newName);
注意:
1:方法的返回值应该是int,表示更新语句所影响的行数
2:在调用的地方必须加事务,没有事务不能正常执行
JPA删除
SQL方式-删除
@Query(value = "delete from r_upa where user_id= ?1 and point_indecs_id in (?2)", nativeQuery = true) @Modifying void deleteByUserAndPointIndecs(Long uid, List<Long> hids);
注意:
执行delete和update语句一样,需要添加@Modifying注解,使用时在Repository或者更上层需要@Transactional注解。
函数(delete)方式-删除
- 直接可以使用delete(id),依据id来删除一条数据
- 也可以使用deleteByName(String name)时,需要添加@Transactional注解,才能使用
- Spring Data JPA的deleteByXXXX,是先select,在整个Transaction完了之后才执行delete
JpaRepository
@NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { /** * Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear * the {@link javax.persistence.EntityManager} after the call. * * @param entities * 批量解绑多个,优势:只会形成一个SQL语句 */ void deleteInBatch(Iterable<T> entities); /** * Deletes all entities in a batch call. */ void deleteAllInBatch(); }
CrudRepository
@NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * Deletes the entity with the given id. * * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@code id} is {@literal null} */ void deleteById(ID id); /** * Deletes a given entity. * * @param entity * @throws IllegalArgumentException in case the given entity is {@literal null}. */ void delete(T entity); /** * Deletes the given entities. * * @param entities * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}. */ void deleteAll(Iterable<? extends T> entities); /** * Deletes all entities managed by the repository. */ void deleteAll(); }
JPA添加
利用JpaRepository和CrudRepository中的 save操作
JpaRepository
@NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ <S extends T> List<S> saveAll(Iterable<S> entities); /** * Flushes all pending changes to the database. */ void flush(); /** * Saves an entity and flushes changes instantly. * * @param entity * @return the saved entity */ <S extends T> S saveAndFlush(S entity); }
CrudRepository
@NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the * entity instance completely. * * @param entity must not be {@literal null}. * @return the saved entity will never be {@literal null}. */ <S extends T> S save(S entity); /** * Saves all given entities. * * @param entities must not be {@literal null}. * @return the saved entities will never be {@literal null}. * @throws IllegalArgumentException in case the given entity is {@literal null}. */ <S extends T> Iterable<S> saveAll(Iterable<S> entities); }
JpaRepository和CrudRepository 的区别
- JpaRepository 中的save方法实现源码:
@Transactional public <S extends T> List<S> save(Iterable<S> entities) { List<S> result = new ArrayList<S>(); if (entities == null) { return result; } for (S entity : entities) { result.add(save(entity)); } return result; }
- CrudRepository 中的save方法源代码
@Transactional public <S extends T> S save(S entity) { if (entityInformation.isNew(entity)) { em.persist(entity);//是新的就插入 return entity; } else { return em.merge(entity); //不是新的merge } }
由源码可知CrudRepository 中的save方法是相当于merge+save ,它会先判断记录是否存在,如果存在则更新,不存在则插入记录
参考来源:http://www.ityouknow.com/springboot/2016/08/20/springboot(%E4%BA%94)-spring-data-jpa%E7%9A%84%E4%BD%BF%E7%94%A8.html
-
-
spring boot 中使用 jpa以及jpa介绍
2017-12-13 10:34:24最近在项目中使用了一下jpa,发现还是挺好用的。这里就来讲一下jpa以及在spring boot中的使用。 在这里我们先来了解一下jpa。1.什么是jpa呢?JPA顾名思义就是Java Persistence API的意思,是JDK 5.0注解或XML描述... -
-
SpringBoot————JPA快速使用
2018-04-14 16:52:35Hibernate与JPA 本篇博客中的web项目选用Hibernate作为持久层框架。在Spring Boot中,我们需要了解另一个概念:JPA 上一句话可能有些歧义,并不是说JPA就是Spring Boot中的概念。而是Java Persistence Api,中文... -
浅谈JPA优缺点
2019-11-13 23:31:26一.JPA的理解 JPA的总体思想和现有hibernate、TopLink,JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术: ORM映射元数据,JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系... -
SpringBoot整合SpringDataJPA
2019-05-23 18:23:05一、SpringBootData JPA介绍 SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA标准操作数据的模块。 SpringData JPA:基于JPA的标准数据... -
SpringBoot重点详解--使用JPA操作数据库
2018-04-22 14:27:25目录 ...JPA & Spring Data JPA 配置Maven依赖 配置数据源和JPA 创建POJO实体 数据持久化 使用Spring Data JPA接口(方式一) CrudRepository PagingAndSortingRepository JpaRepository ... -
SpringDataJpa使用详解
2020-02-26 14:16:46Jpa是什么?他是一套规范,类似restful风格一样,都是一套规范。使用Jpa必须按照它的规范来操作数据访问。 SpringDataJpa==Jpa规范+Hibernate底层 为什么要用jpa Jpa适合什么样的项目呢?我觉得适合非互联网项目,... -
jpa笔记
2020-09-05 10:25:25继承JpaRepository接口(SpringDataJPA提供的简单数据操作接口) 继承JpaSpecificationExecutor接口 (SpringDataJPA提供的复杂查询接口) 【2】自定义sql ##@Query是用来配置自定义SQL的注解,后面参数nativeQuery... -
JPA - jpa简介
2018-12-01 21:56:51JPA是什么? JPA:Java Persistence API:用于对象持久化 API Java EE5.0平台标准的ORM规范,使得应用程序以统一的方式访问持久层 Java应用程序根据每个持久化框架的调用方式不同,需要写不同的调用实现。 而JPA... -
JPA教程:JPA概述、JPA实体生命周期、JPA实体映射关系、JPA查询语言
2017-08-01 19:26:17JPA教程:JPA概述、JPA实体生命周期、JPA实体映射关系、JPA查询语言 -
SpringBoot非官方教程 | 第四篇:SpringBoot 整合JPA
2017-04-23 20:37:15JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发人员可以编码来实现该API... -
Spring Data JPA 之 JpaRepository
2018-04-01 23:54:20JpaRepository是Spring提供的非常强大的基本接口。 1 JpaRepository 1.1 JpaRepository接口定义 JpaRepository接口的官方定义如下: public interface JpaRepository<T, ID> extends ... -
springboot 整合 Mybatis、JPA、Redis
2020-07-01 15:04:16下面我们分别整合mysql ,spring data jpa 以及redis 。让我们感受下快车道。 我们首先创建一个springboot 项目,创建好之后,我们来一步步的实践。 使用mybatis 引入依赖: <dependency> <groupId>org.... -
JPA(一):十分钟入门 JPA
2018-09-29 22:01:20一.JPA的概念 为了节省时间,更加具体的解释我们就略过吧。 二.在IDEA中使用JPA 2.1.添加JAP依赖 添加相关的maven依赖 <properties> <project.build.sourceEncoding>UTF-8&... -
eclipse安装JPA插件并添加JPA支持
2018-04-18 16:50:49JPA与ORM什么关系 ORM是JPA规范中的一个体现思想,JPA规范包含了ORM,Sun公司为了简化现有Java EE和Java SE应用的对象持久化的开发工作,整合ORM技术,结束现在Hibernate、iBATIS、TopLink等ORM框架各自为营的局面... -
SpringBoot JPA(JpaRepository)动态查询 分页展示
2018-11-01 10:22:02大家知道Hibernate可以很轻松的根据提供条件进行动态筛选查询,那个JPA怎么实现呢,其中最为简单的就是使用Specification实现JPA的动态查询,本人也是初步接触JPA,第一次使用JPA实现动态查询,因为其动态查询参数... -
Spring Data JPA与JPA和hibernate的关系
2020-10-14 21:04:34JPA只是一套规范,真正干活的还是Hibernate,因为Hibernate实现了JPA这套规范. JPA和Hibernate 的关系就像JDBC和JDBC 驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代... -
SpringDataJPA之JpaRepository和JpaSpecificationExecutor接口
2019-05-18 17:46:15文章目录JpaRepository1.创建接口2.单元测试JpaSpecificationExecutor1.创建接口2.具体功能2.1 单条件查询2.2 多条件查询2.3 分页2.4 排序2.5 分页排序 本文我们来介绍下SpringDataJPA继承结构中剩下的两个接口 ... -
JPA系列之对象持久化API JPA简介
2017-01-20 15:04:05JPA:Java Persistence API,用于对象持久化的 API注意:JPA是规范,不是ORM框架,是ORM框架的规范,JPA没有实现ORM,具体实现由ORM厂商提供现在JPA具体实现框架有:Hibernate、OpenJPA、TopLinkJPA的优势:标准化: ... -
SpringDataJPA之@Query with LIKE(JPA模糊查询)
2018-06-13 16:51:48对于JPA来说,like查询是不友好的,,例如这么查,是查不出来东西的 @Query("select u from user u where u.username like '%username%'") List&lt;User&gt; ... -
springboot jpa
2018-08-27 16:33:57前面有一篇博客说,springboot 2.0.3 jpa是存在问题的,后用springboot jpa 2.0.4 发现此问题已解决.springboot 2.0.4的jpa的sql看起来也比较容易理解,不再是日志中不再是hsql语句,而是传统sql,所以springboot jpa ... -
JPA之Spring Data JPA
2018-09-30 11:38:03JPA是Java Persistence API的简称,也称为Java持久层API,已成为目前比较流行的一种ORM技术之一,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 目录 1.背景 2.优势 ... -
SpringDataJPA 系列之 JPA 简介
2020-07-29 00:06:44JPA 是 Java Persistence API 的简称,中文名为 Java 持久层 API,是 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
-
SoccerNet.txt
-
Bootstrap学习01
-
Ipage分页和PageHelper分页
-
Golang的演化历程
-
【爱码农】C#制作MDI文本编辑器
-
equalsIgnoreCase( )方法
-
用Go语言来写区块链(一)
-
记录pagehelper 出现 :For input string: “list“
-
微服务架构的访问关系
-
零基础极简以太坊智能合约开发环境搭建并开发部署
-
FRM2-quicksheet-2020.pdf
-
MySQL 主从复制 Replication 详解(Linux 和 W
-
运行时数据区(未完待续)
-
ITIL V3 Foundation 最新完整中文培训课件(2017版).pdf
-
ORBBEC DaBai Datasheet _v1.4.pdf
-
使用示波器的过程中遇到的常见问题(下)
-
多线程获取鼠标位置后结束线程
-
idea设置背景图
-
CISCO GUI ipsec 积极模式示例.pdf
-
使用FastJson时字段大小写转换