JPA
一、概念介绍
JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成。JPA 通过 JDK 5.0 注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
二、开发包介绍
由于 JPA 是 sun 公司制定的 API 规范,所以我们不需要导入额外的 JPA 相关的 jar 包,只需要导入 JPA 的提供商的 jar 包。我们选择 Hibernate 作为 JPA 的提供商,所以需要导入 Hibernate 的相关 jar 包。下载网址在官网
三、玩转 JPA 小 demo
需求:保存番剧到数据库
1.搭建开发环境
这里使用的是 IDEA 社区版,创建了个 maven 项目
2.导入 maven 依赖(坐标)
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
3.实体类和数据库表的映射配置
package com.tobeshrek.entity;
import javax.persistence.*;
/**
* 番剧实体类
* 配置映射关系
* 1.实体类和表的映射关系
* 2.实体类中属性和表中字段的映射关系
*/
@Entity//声名实体类
@Table(name = "anime")//建立实体类和表之间的关系
public class Anime {
/**
* 主键生成策略
* GenerationType.IDENTITY :自增,底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增,如MySQL。
* GenerationType.SEQUENCE : 序列,底层数据库必须支持序列,如Oracle。
* GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
*/
@Id//声名主键的配置
@GeneratedValue(strategy = GenerationType.IDENTITY)//声名主键生成策略为自动增长
@Column(name = "anime_id")//声名配置 实体类属性 和 表中字段 的映射关系,name为数据库中字段的名称,不可省略
private Long animeId;//主键ID
@Column(name = "title")
private String title;//番剧标题
@Column(name = "intro")
private String intro;//番剧简介
// set 和 get 方法
public Long getAnimeId() {
return animeId;
}
public void setAnimeId(Long animeId) {
this.animeId = animeId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
}
4.配置 JAP 的核心配置文件
在 Java 工程的 resource 路径下创建一个名为 META-INF 的文件夹,在此文件夹下创建一个名为 persistence.xml 的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<!--需要配置persistence-unit节点
持久化单元:
name:持久化单元名称
transaction-type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="animePersistenceUnitJPA" transaction-type="RESOURCE_LOCAL">
<!--JPA的实现方式,这里选用hibernate实现-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--可选配置:配置jpa实现方的配置信息-->
<properties>
<!-- 数据库信息
用户名,javax.persistence.jdbc.user
密码, javax.persistence.jdbc.password
驱动, javax.persistence.jdbc.driver
数据库地址 javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="mima"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<!--配置jpa实现方(hibernate)的配置信息
自动创建数据库表 : hibernate.hbm2ddl.auto
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
none :不会创建表
显示sql : false|true
格式化显示的sql : false|true
数据库方言
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show.sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>
</persistence>
5.编写测试类
流程:除了第四步不相同,其它基本相同,下面只提取第四部重要的部分
//1.加载配置文件创建工厂(实体管理器工厂)对象
entityManagerFactory = Persistence.createEntityManagerFactory("animePersistenceUnitJPA");
//2.通过实体管理器工厂获取实体管理器
entityManager = entityManagerFactory.createEntityManager();
//3.获取事务对象,开启事务
transaction = entityManager.getTransaction();
transaction.begin();
//4.完成增删改查操作
Anime anime = new Anime();
anime.setTitle("紫罗兰永恒花园");
anime.setIntro("一位少女追寻爱的故事");
entityManager.persist(anime);
//5.提交事务(回滚事务)
transaction.commit();
//6.释放资源
if(entityManager.isOpen()) entityManager.close();
//if(entityManagerFactory.isOpen()) entityManagerFactory.close();
//根据id查询番剧,在调用find方法的时候,就会发送sql语句查询数据库
Anime anime = entityManager.find(Anime.class, 1L);
//根据id查询番剧,延迟加载(懒加载),得到的是一个动态代理对象,什么时候用,什么使用才会查询
Anime anime = entityManager.getReference(Anime.class, 1L);
//测试删除番剧,先查询后删除
Anime anime = entityManager.getReference(Anime.class, 1L);
entityManager.remove(anime);
//测试更新,先查询后更新
Anime anime = entityManager.getReference(Anime.class, 1L);
anime.setTitle("幸运星");
anime.setIntro("女の子の日常");
entityManager.merge(anime);
/**
* 添加番剧
*/
@Test
public void add(){
//4.完成增删改查操作
Anime anime = new Anime();
anime.setTitle("紫罗兰永恒花园");
anime.setIntro("一位少女追寻爱的故事");
Anime anime1 = new Anime();
anime1.setTitle("幸运星");
anime1.setIntro("女の子の日常らしい");
Anime anime2 = new Anime();
anime2.setTitle("利兹与青鸟");
anime2.setIntro("讲述友情");
Anime anime3 = new Anime();
anime3.setTitle("女子高中生");
anime3.setIntro("女子高中生");
entityManager.persist(anime);
entityManager.persist(anime1);
entityManager.persist(anime2);
entityManager.persist(anime3);
}
/**
* 查询全部
* jpql:from com.tobeshrek.Anime
* sql:SELECT * FROM anime
*/
@Test
public void testFindAll() {
//定义jpql语句
String jpql = "from Anime";
//创建查询对象
Query query = entityManager.createQuery(jpql);
//查询结果
List<Anime> list = query.getResultList();
System.out.println("查询结果总数"+list.size());
//打印结果
for (Anime anime : list) {
System.out.println(anime);
}
}
/**
* 排序查询: 倒序查询全部番剧(根据id倒序)
* sql:SELECT * FROM anime ORDER BY anime_id DESC
* jpql:from Anime order by animeId desc
*
* 进行jpql查询
* 1.创建query查询对象
* 2.对参数进行赋值
* 3.查询,并得到返回结果
*/
@Test
public void testFindAllOrder() {
//定义jpql语句
String jpql = "from Anime order by animeId desc";
//创建查询对象
Query query = entityManager.createQuery(jpql);
//查询结果
List<Anime> list = query.getResultList();
System.out.println("查询结果总数"+list.size());
//打印结果
for (Anime anime : list) {
System.out.println(anime);
}
}
/**
* 使用jpql查询,统计客户的总数
* sql:SELECT COUNT(anime_id) FROM anime
* jpql:select count(animeId) from Anime
*/
@Test
public void testCount(){
//定义jpql语句
String jpql = "select count(animeId) from Anime";
//创建查询对象
Query query = entityManager.createQuery(jpql);
//发送查询
Object o = query.getSingleResult();
//打印结果
System.out.println("查询总数"+o);
}
/**
* 分页查询
* sql(MySql):select * from anime limit 0,2
* jpql:from Anime
*/
@Test
public void testPaged(){
//创建查询对象
Query query = entityManager.createQuery("from Anime");
//设置分页条件
query.setFirstResult(0); //从第一(0+1)页开始
query.setMaxResults(2); //每页显示2条
//发送语句
List animeList = query.getResultList();
//打印结果
for (Object o : animeList) {
System.out.println(o);
}
}
/**
* 条件查询
* 案例:查询番剧简介以‘女’开头("女%")的番剧
* sql:SELECT * FROM anime WHERE intro LIKE ?
* jpql : from Anime where intro like ?
*/
@Test
public void testCondition(){
//创建查询对象
Query query = entityManager.createQuery("from Anime where intro like ?");
//设置查询条件,占位符的索引位置(从1开始)
query.setParameter(1,"女%");
//发送语句
List animeList = query.getResultList();
//打印结果
for (Object o : animeList) {
System.out.println(o);
}
}
Spring Data JPA
概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
与 hibernate 之间的关系
JPA 是一套规范,内部是有接口和抽象类组成的。hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以称 hibernate 为 JPA 的一种实现方式,我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,是在 JPA 规范下的专门用来进行数据持久化的解决方案。
搭建开发环境,常规数据库CRUD
1.导入 maven 坐标
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
2.整合 Spring Data JPA 与 Spring
在 resource 文件夹下创建 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--spring 和 spring data jpa的配置-->
<!--1.创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="mima"></property>
<property name="jdbcUrl" value="jdbc:mysql:///jpa" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<!-- 2.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--配置的扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="com.tobeshrek.entity" />
<!-- jpa的实现厂家 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表 -->
<property name="generateDdl" value="false" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
<property name="format_sql" value="true" />
<!--自动创建数据库表-->
<property name="hbm2ddl.auto" value="update" />
</bean>
</property>
<!--jpa的方言 :高级的特性 -->
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<!--3.整合spring dataJpa-->
<jpa:repositories base-package="com.tobeshrek.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory" ></jpa:repositories>
<!--4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 4.txAdvice-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 5.aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.tobeshrek.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--5.声明式事务 -->
<!-- 6. 配置包扫描-->
<context:component-scan base-package="com.tobeshrek" ></context:component-scan>
</beans>
3.使用 JPA 注解配置映射关系
已配置
4,编写符合 Spring Data JPA 规范的 Dao 层接口
/**
* JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作
* JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作)
*/
public interface AnimeDao extends JpaRepository<Anime,Long>, JpaSpecificationExecutor<Anime> {
}
5.完成基本 CRUD 操作
@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class AnimeDaoTest {
@Autowired
private AnimeDao animeDao;
/**
* 添加番剧
*/
@Test
@Transactional
@Rollback(value = false)
public void add(){
Anime anime = new Anime();
anime.setTitle("玉子市场");
anime.setIntro("玉子爱情故事");
animeDao.save(anime);
}
/**
* 查询番剧
* findOne 对应 find方法
* getOne 对应 getReference方法
*/
@Test
@Transactional
@Rollback(value = false)
public void getOne(){
Anime anime = animeDao.getOne(4L);
System.out.println(anime);
}
/**
* 更新,查询修改再保存
*/
@Test
@Transactional
@Rollback(false)
public void update(){
Anime anime = animeDao.getOne(3L);
anime.setTitle("CLANNAD");
anime.setIntro("这是一个讲述亲情和爱情的故事");
animeDao.save(anime);
}
/**
* 删除
*/
@Test
@Transactional
@Rollback(false)
public void delete(){
animeDao.delete(4L);
}
}
6.spring data jpa 实现原理
简单来说就是灵活运用了动态代理,所有的 Dao 接口用一种代理对象实现
7.JPQL 查询
使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询
@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可
//@Query 使用jpql的方式查询。
@Query(value="from Anime")
public List<Anime> findAllJPQL();
//@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引
@Query(value="from Anime where title = ?1")
public Anime findOneJPQL(String titles);
//测试代码
@Test
@Transactional
@Rollback(false)
public void testFindJPQL(){
List<Anime> list = animeDao.findAllJPQL();
for (Anime anime : list) {
System.out.println(anime);
}
Anime anime = animeDao.findOneJPQL("幸运星");
System.out.println(anime);
}
也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询
//@Query 使用jpql的方式更新
@Query("update Anime set intro=?2 where title=?1")
@Modifying//方法参数一般和查询语句占位符顺序相同,不相同如上指定位置
public void updateIntroByTitle(String title, String intro);
//测试代码
@Test
@Transactional
@Rollback(false)
public void testUpateJPQL(){
Anime anime = animeDao.getOne(3L);
System.out.println(anime);//Anime{animeId=3, title='CLANNAD', intro='这是一个讲述亲情和爱情的故事'}
animeDao.updateIntroByTitle("CLANNAD","触动人心的亲情和令人羡慕的爱情故事");
}
8.使用SQL语句查询
Spring Data JPA同样也支持sql语句的查询
//使用原生SQL查询
@Query(value="select * from anime",nativeQuery=true)//nativeQuery : 使用本地sql的方式查询
public List<Anime> findALLBySql();
//测试代码
@Test
@Transactional
@Rollback(false)
public void testSQL(){
List<Anime> list = animeDao.findALLBySql();
for (Anime anime : list) {
System.out.println(anime);
}
}
9.方法命名规则查询
根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
具体的关键字,使用方法和生产成SQL如下表所示:
//方法命名方式查询(根据番剧标题查询番剧)实体类属性第一个字母大写
public Anime findByTitle(String title);
//测试代码
Anime anime = animeDao.findByTitle("CLANNAD");
System.out.println(anime);
复杂CRUD操作
1.Specifications 动态查询
有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象
//JpaSpecificationExecutor中定义的方法
//根据条件查询一个对象
T findOne(Specification<T> spec);
//根据条件查询集合
List<T> findAll(Specification<T> spec);
//根据条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//排序查询查询
List<T> findAll(Specification<T> spec, Sort sort);
//统计查询
long count(Specification<T> spec);
对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
Specification接口中只定义了如下一个方法
/**
* root :Root接口,代表查询的根对象,可以通过root获取实体中的属性
* query :代表一个顶层查询对象,用来自定义查询
* cb :用来构建查询,此对象里有很多条件方法
**/
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
2.使用Specifications完成条件查询
@Test
@Transactional
@Rollback(false)
public void testSpecifications(){
//使用匿名内部类的方式,创建一个Specification的实现类,并实现toPredicate方法
Specification<Anime> spec = new Specification<Anime>() {
public Predicate toPredicate(Root<Anime> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//root:从实体Customer对象中按照custName属性进行查询
Path<Object> title = root.get("title");//注意这里是实体类属性
//cb:构建查询,添加查询方式 like:模糊匹配
//Predicate predicate = cb.equal(title, "CLANNAD");//根据标题精准匹配
Predicate predicate = cb.like(title.as(String.class),"C%");//根据标题模糊查询
return predicate;
}
};
//发送语句
//Anime anime = animeDao.findOne(spec);//根据标题精准匹配
//System.out.println(anime);
List<Anime> animeList = animeDao.findAll(spec);//根据标题模糊查询
for (Anime anime : animeList) {
System.out.println(anime);
}
}
3.使用Specifications的分页查询
/**
* 使用Specifications的分页查询
*/
@Test
public void testPage(){
/**
* 构造分页参数
* Pageable : 接口
* PageRequest实现了Pageable接口,调用构造方法的形式构造
* 第一个参数:页码(从0开始)
* 第二个参数:每页查询条数
*/
Pageable pageable = new PageRequest(0,2);
//发送语句
Page<Anime> page = animeDao.findAll(pageable);
System.out.println("总记录数:"+page.getTotalElements());//总记录数:3
System.out.println("总页数:"+page.getTotalPages()); //总页数:2
for (Anime anime : page.getContent()) { //Anime{animeId=1, title='紫罗兰永恒花园', intro='一位少女追寻爱的故事'}
System.out.println(anime); //Anime{animeId=2, title='幸运星', intro='女の子の日常らしい'}
}
}
对于Spring Data JPA中的分页查询,是其内部自动实现的封装过程,返回的是一个Spring Data JPA提供的pageBean对象。其中的方法说明如下
//获取总页数
int getTotalPages();
//获取总记录数
longgetTotalElements();
//获取列表数据
List<T> getContent();
4.方法对应关系
多表之间的关系和操作多表的操作步骤
1.表关系
一对一
就是特殊的一对多
一对多
一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
多对多
中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键
2.实体类中的关系
包含关系:可以通过实体类中的包含关系描述表关系
继承关系
3.分析步骤
1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,再实体类中描述表关系(包含关系)
4.配置映射关系
4.完成多表操作
新建类,配置好属性和数据库映射
用户表user
/**
* 用户实体类
*/
@Entity
@Table(name = "user")
public class User {
/**
* 主键ID,自增
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
/**
* 用户名
*/
@Column(name = "username")
private String username;
/**
* 用户密码
*/
@Column(name = "password")
private String password;
}
一对多操作
一的一方均不维护主键
/**
* 使用注解的形式配置多表关系
* 1.声明关系
* @OneToMany : 配置一对多关系
* targetEntity :对方对象的字节码对象
* 2.配置外键(中间表)
* @JoinColumn : 配置外键
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
* 放弃外键维护权
* mappedBy:对方配置关系的属性名称\
* cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
* CascadeType.all : 所有
* MERGE :更新
* PERSIST :保存
* REMOVE :删除
* fetch : 配置关联对象的加载方式
* EAGER :立即加载
* LAZY :延迟加载
*/
配置用户与番剧的一对多
//User.java
//配置用户和番剧的一对多关系
@OneToMany(mappedBy = "user",cascade = CascadeType.MERGE)
private Set<Anime> Animes = new HashSet<Anime>();
//Anime.java
//多对一关系映射:多个番剧对应用户
@ManyToOne(targetEntity = User.class,fetch = FetchType.LAZY,cascade = CascadeType.ALL)
@JoinColumn(name = "user_anime_id",referencedColumnName = "user_id")//这里是对方数据库字段名称
private User user;//用它的主键,对应联系人表中的外键
创建DAO层,代码不贴了
测试代码
//新建二次猿对象
User user = new User();
user.setUsername("平泽唯");
user.setPassword("123");
//新建番剧对象
Anime anime = new Anime();
anime.setTitle("轻音少女");
anime.setIntro("喝茶部日常");
//设置关系
anime.setUser(user);
//保存
animeDao.save(anime);
多对多操作
这里用User和Goods的多对多
创建Goods实体类并配置映射关系
/**
* 周边实体类
*/
@Entity
@Table(name = "goods")
public class Goods {
/**
* 主键ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "goods_id")
private Long goodsId;
/**
* 商品标题
*/
@Column(name = "title")
private String title;
/**
* 商品价格
*/
@Column(name = "price")
private Float price;
}
配置多对多关系
/**
* 配置用户到角色的多对多关系
* 配置多对多的映射关系
* 1.声明表关系的配置
* @ManyToMany(targetEntity = Role.class) //多对多
* targetEntity:代表对方的实体类字节码
* 2.配置中间表(包含两个外键)
* @JoinTable
* name : 中间表的名称
* joinColumns:配置当前对象在中间表的外键
* @JoinColumn的数组
* name:外键名
* referencedColumnName:参照的主表的主键名
* inverseJoinColumns:配置对方对象在中间表的外键
*/
//User.java
//配置和周边表多对多关系映射
@ManyToMany(mappedBy="users",cascade = CascadeType.ALL)
private Set<Goods> goodsSet = new HashSet<Goods>();
//Goods.java
//多对多关系映射
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="user_goods",//中间表的名称
//中间表user_goods字段关联goods表的主键字段goods_id
joinColumns={@JoinColumn(name="goods_id",referencedColumnName="goods_id")},
//中间表user_goods的字段关联user表的主键user_id
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")})
private Set<User> users = new HashSet<User>();
//测试代码
//新建二次猿对象
User user = new User();
user.setUsername("秋山mio");
user.setPassword("123");
//新建周边对象
Goods goods = new Goods();
goods.setTitle("康娜手办");
//设置关系
goods.getUsers().add(user);
//保存
goodsDao.save(goods);
对象导航查询
对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。
