
JPA
使用规范说明书
科大讯飞股份有限公司
2017年10月26日
版本记录
| 序号 | 版本 | 更改时间 | 更改内容描述 | 作者 |
| 1 | 1.0 | 2017-10-26 | 新建 | 吴海林 |
| 2 | 2.0 | 2017-11-10 | 评审后修改 | 吴海林 |
1、JPA概述
1.1 什么是JPA规范
JPA规范(java持久化API)JPA和Hibernate等框架一样,都是java持久化解决方案,负责把数据保存到数据库。不同的是,JPA只是一种标注,规范,而不是框架。JPA自己没有具体的实现。使用JPA后,程序不在依赖于某种ORM框架。具体实现有TopLink,Eclipselink, OpenJPA等。
随着ibatis,hibernate等优秀的ORM管理框架的出现,相比以上供应商,使得JPA性能有了很大的提升。
1.2 JPA原理实现
我们知道JPA 只是一个规范,下面的 JPA Provider 可以由不同的 ORM 框架提供。ORM 框架可以根据映射关系,操作PO对象,自动生成增查改删的 SQL
JPA包括以下3方面的技术:
1. ORM映射元数据,JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
2. JPA的API,用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
3. 查询语言,这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
使用JPA的优点也是有很多:继承Repository接口,在注解中书写JPQL语句即可访问数据库;支持方法名解析方式访问数据库;使用Predicate支持动态查询
JPA当访问数据库主要工作
1.得到JDBC驱动程序
2.得到持久性提供者相关类库和配置文件
3.提供实体类
4.使用Persistence、EntityManagerFactory和Entity等接口。
2、JPA使用
2.1 使用前的准备及配置
2.1.1 原生配置方式java-persistence
Maven依赖:
配置文件(MATA-INF>下的persistence.xml)
怎么去掉persistence.xml的实体类配置?
使用了 persistence 配置文件,去掉“persistenceUnitName”属性,添加“packagesToScan”属性,persistence.xml配置文件中的persistence-unit名字照样保留,但是 persistence 配置文件中不需要对实体类进行配置,会自动识别。
2.1.2 SpringDataJPA+hibernate(使用最多)
Maven依赖:(除spring核心相关包之外)
配置文件(定义EntityManagerFactory实体类管理)
2.1.3 SpringBoot+JPA
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {}
Application.properties(resources下面即可)
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.1.12:3306/jeeboot?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
…….
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.jdbc.batch_size=25
spring.jpa.properties.hibernate.jdbc.fetch_size=50
spring.jpa.properties.hibernate.auto_quote_keyword=true
spring.jpa.properties.hibernate.current_session_context_class=thread
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
2.2 JPA的API
| 单元 | 描述 |
| EntityManagerFactory | 这是一个EntityManager的工厂类。它创建并管理多个EntityManager实例。 |
| EntityManager | 这是一个接口,它管理的持久化操作的对象。它的工作原理类似工厂的查询实例。 |
| Entity | 实体是持久性对象是存储在数据库中的记录。 |
| EntityTransaction | 它与EntityManager是一对一的关系。对于每一个EntityManager,操作是由EntityTransaction类维护。 |
| Persistence | 这个类包含静态方法来获取EntityManagerFactory实例。 |
| Query | 该接口由每个JPA供应商,能够获得符合标准的关系对象。 |
EntityManagerFactory factory = Persistence.createEntityManagerFactory("JPAService");
EntityManager em = factory.createEntityManager();
Query query = em.createQuery("select p from CourtInfo p where p.id=:Id");
query.setParameter("Id",new Integer(1));
query.getSingleResult();
query.getResultList();
2.2.1 实体类Entity
表对象entity可手写亦可工具生成(建议手写)
实体类用java注解来配置也相当的简单。实体bean必须序列化。
@Entity
持久化实体类的声明-该声明是必须的,name属性可选
@Table
对表和实体类的声明,当名称不一致时必须注解声明
name 属性表示实体所对应表的名称,默认表名为实体名称。
无论表名与实体名是否一致,建议添加name属性;
catalog 和schema 属性表示实体指定的目录名或数据库名,这个根据不同的数据类型有所不同。
uniqueConstraints 唯一约束条件
@NamedQuery中的属性name指定命名查询的名称,query属性指定命名查询的语句。
如果要定义多个命名查询,需要使用@NamedQueries。
定义好命名查询后,可以使用EntityManager的createNamedQuery方法传入命名查询的名称创建查询。例如:createNamedQuery("findAllUser");--建议不用@NamedQuery该注解
@Id
主键声明
有一点需要注意的是实体类主键最好使用可以为null的包装类型,例如Integer Long等
@GeneratedValue
标注有以两个属性:
(1)strategy 属性表示生成主键的策略 ,有4种类型,分别定义在枚举型GenerationType中,其中JPA提供了以下几种ID生成策略
GeneratorType.AUTO ,由JPA自动生成
GenerationType.IDENTITY,使用数据库的自增长字段,需要数据库的支持(如SQL Server、MySQL、DB2、Derby等)
GenerationType.SEQUENCE,使用数据库的序列号,需要数据库的支持(如Oracle)
GenerationType.TABLE,使用指定的数据库表记录ID的增长 需要定义一个TableGenerator,在@GeneratedValue中引用。
(2)generator 为不同策略类型所对应的生成规则名,它的值根据不同的策略有不同的设置。
@Column-对表字段和实体字段的声明,当名称不一致时必须注解声明
标记可以标注在Getter方法或属性前。
无论字段名与变量是否一致,建议添加name属性,且放置在属性前;
unique 属性表示该字段是否为唯一标识,默认为false。如果表中有一个字段需要唯一标识,则既可以使用@Column标记也可以使用@Table标记中的
nullable 属性表示该字段是否可以为null值,默认为true(允许为null值)。
insertable 属性表示在使用“INSERT” SQL语脚本插入数据时,是否需要插入该字段的值。
updatable 属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读属性,例如主键和外键等。这些字段值通常是自动生成的。
length 属性表示该字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。
precision 属性和scale 属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数
@Temporal设置为日期Date类型的字段
@Enumerated,如果想要将该字段映射为String(枚举表示的String),可以将其value设置为EnumType.String。
@Basic标记可以指定实体属性的加载方式LAZY和EAGER
@Transient 批注指定实体的非持久字段或属性
一些一对多,多对多等对象映射关系的注解,这里不做介绍了,实际数据库设计应避免这种映射关系设计。每张表都有独属于自己的主键Id。
JPA实体生命周期:
JPA实体生命周期分为4种状态,分为:新建,受管(托管),分离(游离),删除。其实跟HIBERNATE的映射实体差不多,大致对应了hibernate中的生命周期:自由状态(Transient)——不受管,持久状态(Persistent)——托管,游离状态(Detached)——分离,只是粒度上有差别。
2.2.2 Dao-Repository层
基础的 Repository 提供了最基本的数据访问功能,单表的CRUD基本操作可以继承这些接口,其几个子接口则扩展了一些功能。它们的继承关系如下:
Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
(1)CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
(2)PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
(3)JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
2.2.3 JPQL语法
JPQL是EJB QL的一种扩展,它是针对实体面向对象的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
JPQL就是一种查询语言,具有与SQL 相类似的特征,JPQL是完全面向对象的,具备继承、多态和关联等特性,和hibernate HQL很相似。
2.2.3.1、sql,hql,jpql之间的区别;
sql是关系数据库查询语言,面对的数据库;而hql是Hibernate这样的数据库持久化框架提供的内置查询语言,虽然他们的目的都是为了从数据库查询需要的数据,但sql操作的是数据库表和字段,而作为面向对象的hql操作的则是持久化类及其属性
jpql:基于首次在EJB2.0中引入的EJB查询语言(EJB QL),Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起.使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL.
JPQL是面向对象查询,格式:from + 类名 + 类对象 + where + 对象的属性
需要注意的是:
A、大小写敏感
因为jpql是面向对象的,而对象类的名称和属性都是大小写敏感的,所以jpql也是大小写敏感的。因此,在编写jpql语句的时候,一定要注意大小写。
B、from子句
from子句的形式和sql基本类似,不过一般会给类名起一个别名(如from Dog d,其中d就是Dog类的对象)
对于多表查询的连接,则和sql完全一样(如from Dog d,Cat c)
2.2.3.2、通过解析方法名创建查询
JpaRepository会对Repository层所有未加@Query的方法名进行校验 不符合规范会报错,除非添加@Query注解
查询方法以find | read | get 开头—建议统一用find开头;
格式findBy**And/Or**;findBy**NotLike等,具体参考下图:
List User findByUserNameAndPassword(String username,String password); 注:按方法名解析的查询方法通常只适用于单表的查询,且建议where条件参数不多于三条的情况下,返回值通常上对应表的实体Bean,通常用实体类List类型;具体要看返回结果,当返回值与类型不匹配时会造成查询错误; 2.2.3.3、使用 @Query 创建自定义查询 @Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可 查询策略: 1:create-if-not-found:如果方法通过@Query指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值 2:create:通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query指定的查询语句,都将会被忽略 3:use-declared-query:如果方法通过@Query指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常 2.2.3.3.1、如何多表查询?并返回自定义值? 返回对象VO @Query(value = "select new pers.zhuch.model.MyModel(u.userName, ui.name, ui.gender, ui.description) from UserInfo ui, User u where u.id = ui.userId") public List 返回Map @Query("select new map(i.userAddress,u.userName,u.password) from User u,UserInfo i where u.id = i.userId and i.userId=?1") List
