author:尹会东start
createTime:2020-06-04

文章目录

  • 一,orm思想和hibernate以及jpa
    • 1.orm思想
    • 2. hibernate框架介绍
    • 3.jpa规范
      • JPA与hibernate的关系
    • 4.jpa的入门案例
      • 1.案例:是客户的相关操作(增删改查)
      • 2.创建项目导入依赖
      • 3.配置文件
      • 4.实体类
      • 5.测试类
        • jpa操作的操作步骤
        • 测试
    • 5.主键生成策略
    • 6.JPA的API介绍
      • 1.Persistence对象
      • 2.EntityManagerFactory
      • 3. EntityManager
      • 4.EntityTransaction
    • 7. 抽取JPAUtil工具类
    • 8. 使用JPA完成增删改查操作
      • 1.保存
      • 2.删除
      • 3.更新
      • 4.根据ID查询
        • 1)立即加载
        • 2)懒加载
      • 5.复杂查询
        • 1)查询全部
        • 2)分页查询
        • 3)条件查询
        • 4)排序查询
        • 5)统计查询
  • 二,springdatajpa的运行原理以及基本操作
    • 1.springDataJpa概述
      • 1.1SpringDataJpa概述
      • 1.2springdatajpa的特性
      • 1.3Spring Data JPA 与 JPA和hibernate之间的关系
    • 2.SpringDataJpa快速入门
      • 2.1需求说明
      • 2.2搭建环境
        • 项目依赖
        • Spring整合SpringDataJPA
        • 关联映射实体类
        • mapper接口
        • 测试类
    • 3.SpringDataJPA的底层代码分析
      • 3.1常用接口分析
      • 3.2SpringDataJPA的实现过程
    • 4.SpringDataJPA的查询方式
      • 4.1使用jpql的方式查询
      • 4.2使用SQL语句查询
      • 4.3方法命名规则查询
  • 三,springdataJpa的动态查询与多表查询
    • 1.动态查询
      • 1.1 使用Specifications完成条件查询
      • 1.2基于Specification的分页查询
      • 1.3方法对应关系
    • 2.多表设计
      • 2.1表之间关系的划分
      • 2.2 在JPA框架中表关系的分析步骤
    • 3.jpa中的一对多
      • 3.1情景模拟
      • 3.2表关系建立
      • 3.3 实体类关系建立以及映射配置
      • 3.4映射的注解说明
        • **@OneToMany:**
        • **@ManyToOne**
        • **@JoinColumn**
      • 3.5一对多的操作
        • 1)添加
        • 2)删除
        • 3)级联操作
    • 4.jpa中的多对多
      • 4.1.情景模拟
      • 4.2表关系建立
      • 4.3实体关系建立以及映射配置
      • 4.4映射的注解说明
        • **@ManyToMany**
        • **@JoinTable**
        • **@JoinColumn**
      • 4.5多对多的操作
    • 5.SpringDataJPA的多表查询
      • 对象导航图查询

一,orm思想和hibernate以及jpa

1.orm思想

主要目的:操作实体类就相当于操作数据库表

建立两个映射关系

实体类

实体类中的属性表中字段的映射关系

不再重点关注: SQL语句

实现了ORM思想的框架:mybatishibernate

2. hibernate框架介绍

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

3.jpa规范

JPA的全称是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。

JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

JPA与hibernate的关系

JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代Hibernate呢?JDBC规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。

4.jpa的入门案例

1.案例:是客户的相关操作(增删改查)

​ 客户:就是一家公司

2.创建项目导入依赖

<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.配置文件

位置:配置到类路径下的一个叫做 META-INF 的文件夹下
命名:persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"><!--配置持久化单元name:持久化单元名称transaction-type:事务类型RESOURCE_LOCAL:本地事务管理JTA:分布式事务管理 --><persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL"><!--配置JPA规范的服务提供商 --><provider>org.hibernate.jpa.HibernatePersistenceProvider</provider><properties><!-- 数据库驱动 --><property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/><!-- 数据库地址 --><property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa"/><!-- 数据库用户名 --><property name="javax.persistence.jdbc.user" value="root"/><!-- 数据库密码 --><property name="javax.persistence.jdbc.password" value="root"/><!--jpa提供者的可选配置:我们的JPA规范的提供者为hibernate,所以jpa的核心配置中兼容hibernate的配 --><!--显示sql语句--><property name="hibernate.show_sql" value="true"/><property name="hibernate.format_sql" value="true"/><!--建表方式create:每次执行都创建表update:没有表才创建none:不创建表--><property name="hibernate.hbm2ddl.auto" value="update"/></properties></persistence-unit>
</persistence>

4.实体类

/*** @author yinhuidong* @createTime 2020-06-01-21:43*/
//作用:指定当前类是实体类。
@Entity
/*** 作用:指定实体类和表之间的对应关系。* 属性:*   name:指定数据库表的名称*/
@Table(name = "t_person")
public class Person {//作用:指定当前字段是主键。/*** @Id*     作用:指定主键的生成方式。。*     属性:*     strategy :指定主键生成策略。*/@GeneratedValue(strategy = GenerationType.IDENTITY)/*** @Column* 作用:指定实体类属性和数据库表之间的对应关系* 属性:*      name:指定数据库表的列名称。*      unique:是否唯一  *      nullable:是否可以为空  *      inserttable:是否可以插入  *      updateable:是否可以更新  *      columnDefinition: 定义建表时创建此列的DDL  *      secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),*      该属性定义该列所在从表的名字搭建开发环境[重点]*/@Column(name = "id")private Integer id;@Column(name = "p_name")private String name;@Column(name = "p_age")private Integer age;@Column(name = "p_email")private String email;@Column(name = "p_birth")private Date birth;public Person() {}public Person(Integer id, String name, Integer age, String email, Date birth) {this.id = id;this.name = name;this.age = age;this.email = email;this.birth = birth;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Date getBirth() {return birth;}public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", email='" + email + '\'' +", birth=" + birth +'}';}
}

5.测试类

jpa操作的操作步骤

1.加载配置文件创建实体管理器工厂Persisitence:静态方法(根据持久化单元名称创建实体管理器工厂)createEntityMnagerFactory(持久化单元名称)作用:创建实体管理器工厂2.根据实体管理器工厂,创建实体管理器EntityManagerFactory :获取EntityManager对象方法:createEntityManager* 内部维护的很多的内容内部维护了数据库信息,维护了缓存信息维护了所有的实体管理器对象再创建EntityManagerFactory的过程中会根据配置创建数据库表* EntityManagerFactory的创建过程比较浪费资源特点:线程安全的对象多个线程访问同一个EntityManagerFactory不会有线程安全问题* 如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题?思路:创建一个公共的EntityManagerFactory的对象* 静态代码块的形式创建EntityManagerFactory3.创建事务对象,开启事务EntityManager对象:实体类管理器beginTransaction : 创建事务对象presist : 保存merge  : 更新remove : 删除find/getRefrence : 根据id查询Transaction 对象 : 事务begin:开启事务commit:提交事务rollback:回滚
4.增删改查操作
5.提交事务
6.释放资源

测试

    @Testpublic void testSave(){EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");EntityManager em = factory.createEntityManager();EntityTransaction tx = em.getTransaction();tx.begin();Person person = new Person(null,"aaa",22,"aaa@163.com",new Date());em.persist(person);tx.commit();}

5.主键生成策略

@GeneratedValue:配置主键的生成策略
strategy
GenerationType.IDENTITY :自增,mysql
底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
GenerationType.SEQUENCE : 序列,oracle
底层数据库必须支持序列
GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略

6.JPA的API介绍

1.Persistence对象

Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory

EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");

2.EntityManagerFactory

EntityManagerFactory 接口主要用来创建 EntityManager 实例

EntityManager em = factory.createEntityManager();

由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory 的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory 即可。

3. EntityManager

在 JPA 规范中,EntityManager是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager将其持久化后才会变成持久化对象。EntityManager对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。我们可以通过调用EntityManager的方法完成获取事务,以及持久化数据库的操作

 getTransaction : 获取事务对象persist : 保存操作merge : 更新操作remove : 删除操作find/getReference : 根据id查询

4.EntityTransaction

在 JPA 规范中, EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单

begin:开启事务
commit:提交事务
rollback:回滚事务

7. 抽取JPAUtil工具类

/*** @author yinhuidong* @createTime 2020-06-02-0:13*/
public class JpaUtils {private static EntityManagerFactory factory;static {factory = Persistence.createEntityManagerFactory("myJpa");}public static EntityManager getEm(){return factory.createEntityManager();}
}

8. 使用JPA完成增删改查操作

1.保存

    /*** 添加*/@Testpublic void testSave(){EntityManager em = JpaUtils.getEm();EntityTransaction tx = em.getTransaction();tx.begin();Person person = new Person(null,"aaa",22,"aaa@163.com",new Date());em.persist(person);tx.commit();em.close();}

2.删除

   /*** 删除:remove*/@Testpublic void testRemove(){EntityManager em = JpaUtils.getEm();EntityTransaction tx = em.getTransaction();tx.begin();Person person = em.getReference(Person.class, 1);em.remove(person);tx.commit();em.close();}

3.更新

    /*** 更新:merge*/@Testpublic void testmerge(){EntityManager em = JpaUtils.getEm();EntityTransaction tx = em.getTransaction();tx.begin();Person person = new Person(1,"bbb",22,"aaa@163.com",new Date());em.merge(person);tx.commit();em.close();}

4.根据ID查询

1)立即加载

    /*** 查询:find* 立即加载,执行find()立刻发出sql*/@Testpublic void testFind(){EntityManager em = JpaUtils.getEm();Person person = em.find(Person.class, 1);System.out.println(person);em.close();}

2)懒加载

    /*** 查询:getReference* 懒加载机制,什么时候调用查询结果什么时候发出sql语句* 得到的是一个动态代理对象*/@Testpublic void testgetReference(){EntityManager em = JpaUtils.getEm();Person person = em.getReference(Person.class, 1);System.out.println(person);em.close();}

5.复杂查询

JPQL全称Java Persistence Query Language

Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性

1)查询全部

    /*** 查询所有*/@Testpublic void test1(){EntityManager em = JpaUtils.getEm();String jpql ="from Person";Query query = em.createQuery(jpql);query.getResultList().forEach(System.out::println);em.close();}

2)分页查询

    /*** 分页查询*/@Testpublic void test1(){EntityManager em = JpaUtils.getEm();String jpql ="from Person";Query query = em.createQuery(jpql);query.setFirstResult(0);query.setMaxResults(2);query.getResultList().forEach(System.out::println);em.close();}

3)条件查询

    /*** 条件查询*/@Testpublic void test1(){EntityManager em = JpaUtils.getEm();String jpql ="from Person where name like ?";Query query = em.createQuery(jpql);query.setParameter(1,"aa%");//获取唯一结果集//System.out.println(query.getSingleResult());query.getResultList().forEach(System.out::println);em.close();}

4)排序查询

    /*** 排序查询*/@Testpublic void test1(){EntityManager em = JpaUtils.getEm();String jpql ="from Person order by id desc";Query query = em.createQuery(jpql);query.getResultList().forEach(System.out::println);em.close();}

5)统计查询

    /*** 统计查询*/@Testpublic void test1(){EntityManager em = JpaUtils.getEm();String jpql ="select count(id) from Person";Query query = em.createQuery(jpql);System.out.println(query.getSingleResult());em.close();}

二,springdatajpa的运行原理以及基本操作

1.springDataJpa概述

1.1SpringDataJpa概述

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!

Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦

1.2springdatajpa的特性

SpringData Jpa 极大简化了数据库访问层代码。 如何简化的呢? 使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。

1.3Spring Data JPA 与 JPA和hibernate之间的关系

JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)

Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。

2.SpringDataJpa快速入门

2.1需求说明

Spring Data JPA完成客户的基本CRUD操作

2.2搭建环境

项目依赖

        <!--hibernate--><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>5.4.15.Final</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>5.0.7.Final</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.3.6.Final</version></dependency><!--springdatajpa--><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><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId><version>2.3.0.RELEASE</version></dependency>

Spring整合SpringDataJPA

<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/data/jpahttp://www.springframework.org/schema/data/jpa/spring-jpa.xsd"><!--组件扫描--><context:component-scan base-package="com.example"><!-- 配置要忽略的注解 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!--Spring整合MyBatis--><!--配置数据源--><!--引入外部属性文件--><context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.DriverName}"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory"/></bean><jpa:repositories base-package="com.example.mapper"transaction-manager-ref="transactionManager"entity-manager-factory-ref="entityManagerFactory"></jpa:repositories><!--事务管理--><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><aop:config><!--配置切入点表达式--><aop:pointcut id="my" expression="execution(* com.example.service.impl.*.* (..))"></aop:pointcut><aop:advisor advice-ref="txAdvice" pointcut-ref="my"></aop:advisor></aop:config><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource" ref="dataSource"></property><property name="packagesToScan" value="com.example.domain"></property><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><property name="database" value="MYSQL"></property><property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property><property name="showSql" value="true"></property></bean></property><property name="jpaDialect"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean></property></bean>
</beans>

关联映射实体类

@Entity
@Table(name = "t_person")
public class Person {@Id@GeneratedValue(strategy = IDENTITY)@Column(name = "id")private Long id;@Column(name = "p_name")private String name;@Column(name = "p_age")private Integer age;@Column(name = "p_email")private String email;@Column(name = "p_birth")private Date birth;
}

mapper接口

/*** @author yinhuidong* @createTime 2020-06-04-15:46* JpaRepository<Person, Long> 用来完成基本CRUD* JpaSpecificationExecutor<Person> 用来完成复杂查询*/
public interface PersonMapper extends JpaRepository<Person, Long>, JpaSpecificationExecutor<Person> {}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestCRUD {@Autowiredprivate PersonMapper mapper;//保存操作@Testpublic void testSave(){mapper.save(new Person("aaa",20,"aaa@qq.com",new Date()));}/*** 修改操作:* 修改与保存共用一个方法save* 当传入的实体类有主键就是修改,没有主键就是保存*/@Testpublic void testUpdate(){Person person = new Person("aaa",20,"bbb@qq.com",new Date());person.setId(8l);mapper.save(person);}//删除@Testpublic void testDelete(){Person person = new Person("aaa",20,"bbb@qq.com",new Date());person.setId(8l);mapper.delete(person);}//根据ID查询@Testpublic void testFindById(){System.out.println(mapper.findById(7l).get());}//查询所有@Testpublic void testFindAll(){mapper.findAll().forEach(System.out::println);}
}

3.SpringDataJPA的底层代码分析

3.1常用接口分析

在自定义mapper接口中,并没有提供任何方法就可以使用其中的方法,那么这些方法究竟是怎么来的?,由于我们的接口继承了JpaRepositoryJpaSpecificationExecutor,所以我们可以使用这两个接口的所有方法。

在使用Spring Data JPA时,一般实现JpaRepositoryJpaSpecificationExecutor接口,这样就可以使用这些接口中定义的方法,但是这些方法都只是一些声明,没有具体的实现方式,那么在 Spring Data JPA中它又是怎么实现的呢?

3.2SpringDataJPA的实现过程

通过刚才的入门案例,打断点来分析SpringDataJPA的执行过程。

以findById()为例。

断点执行到方法上时,我们可以发现注入的是personmapper对象,本质上是通过jdk的动态代理生成的一个对象。

查看具体过程:

当程序执行的时候,会通过JdkDynamicAopProxyinvoke方法,对Dao对象生成动态代理对象。根据对Spring Data JPA介绍而知,要想进行findOne查询方法,最终还是会出现JPA规范的API完成操作,那么这些底层代码存在于何处呢?答案很简单,都隐藏在通过JdkDynamicAopProxy生成的动态代理对象当中,而这个动态代理对象就是SimpleJpaRepository,通过 SimpleJpaRepository的源码分析,定位到了findOne方法,在此方法中,返回em.find()的返回结果,那么em又是什么呢?

继续查找em对象,我们发现em就是EntityManager对象,而他是JPA原生的实现方式,所以我们得到结论Spring Data JPA只是对标准JPA操作进行了进一步封装,简化了Dao层代码的开发。

由此可得:SpringDataJpa完整执行流程:

SpringDataJpa–>JPA–>hibernate–>数据库

4.SpringDataJPA的查询方式

4.1使用jpql的方式查询

使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询

@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可

    /*** 测试使用JPQL的方式进行查询*/@Query("from Person ")List<Person> MyfindAll();@Query("from Person where id=?1")Person MyfindById(Long id);@Query(value = "update Person set name=?1 where id=?2")@Modifying@Transactionalvoid MyUpdate(String name,Long id);

4.2使用SQL语句查询

    /*** nativeQuery : 使用本地sql的方式查询*/@Query(value = "select * from t_person",nativeQuery = true)List<Person> MyfindAll();@Query(value = "select * from t_person where id=?1",nativeQuery = true)Person MyfindById(Long id);@Query(value = "update t_person set p_name=?1 where id=?2",nativeQuery = true)@Modifying@Transactionalvoid MyUpdate(String name,Long id);

4.3方法命名规则查询

方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询

按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

Keyword Sample JPQL
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)

三,springdataJpa的动态查询与多表查询

1.动态查询

有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

import java.util.List;import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;/***   JpaSpecificationExecutor中定义的方法**/public interface JpaSpecificationExecutor<T> {//根据条件查询一个对象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);

1.1 使用Specifications完成条件查询

    //动态条件查询@Testpublic void test7(){//使用匿名了内部类的方式,创建一个Specification的实现类,并实现toPredicate()Specification<Person> sp=new Specification<Person>() {@Overridepublic Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {//criteriaBuilder:构建查询,添加查询方式 like:模糊匹配return criteriaBuilder.like(root.get("name").as(String.class),"少妇白洁");}};mapper.findAll(sp).forEach(System.out::println);}

1.2基于Specification的分页查询

    //动态分页查询@Testpublic void test8(){//构建查询条件Specification<Person> sp=new Specification<Person>() {@Overridepublic Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {return criteriaBuilder.like(root.get("name").as(String.class),"少妇%");}};/*** 构建分页参数:*  Pageable:接口*  PageRequet实现了Pageable接口,调用of方法的形式构造。*      第一个参数:页码(从0开始)*      第二个参数:每页查询条数*/Pageable request = PageRequest.of(0,1);/*** 分页查询:封装为SpringDataJpa内部的pageBean* 此重载findAlll方法为分页方法需要两个参数*      第一个参数、;查询条件*      第二个参数:分页参数*/Page<Person> page = mapper.findAll(sp, request);page.forEach(System.out::println);}

1.3方法对应关系

方法名称 Sql对应关系
equle filed = value
gt(greaterThan ) filed > value
lt(lessThan ) filed < value
ge(greaterThanOrEqualTo ) filed >= value
le( lessThanOrEqualTo) filed <= value
notEqule filed != value
like filed like value
notLike filed not like value

2.多表设计

2.1表之间关系的划分

数据库中多表之间存在着三种关系,如图所示。

从图可以看出,系统设计的三种实体关系分别为:多对多、一对多和一对一关系。注意:一对多关系可以看为两种: 即一对多,多对一。所以说四种更精确。

2.2 在JPA框架中表关系的分析步骤

在实际开发中,我们数据库的表难免会有相互的关联关系,在操作表的时候就有可能会涉及到多张表的操作。而在这种实现了ORM思想的框架中(如JPA),可以让我们通过操作实体类就实现对数据库表的操作。所以学习重点是:掌握配置实体之间的关联关系。

第一步:首先确定两张表之间的关系。

​ 如果关系确定错了,后面做的所有操作就都不可能正确。

第二步:在数据库中实现两张表的关系

第三步:在实体类中描述出两个实体的关系

第四步:配置出实体类和数据库表的关系映射(重点)

3.jpa中的一对多

3.1情景模拟

上个案例我创建的是一张Person表,此时在创建一张Dog表,每个人都可以对应着多个宠物狗。

此时从人的角度就是一对多的关系。

3.2表关系建立

在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束。

什么是外键?

指的是从表中有一列,取值参照主表的主键,这一列就是外键。

一对多数据库关系的建立,如下图所示

3.3 实体类关系建立以及映射配置

数据库建表语句

CREATE TABLE t_person(
id INT PRIMARY KEY AUTO_INCREMENT,
p_age INT ,
p_birth DATE,
p_email VARCHAR(20),
p_name VARCHAR(20)
);
CREATE TABLE t_dog(
d_id INT PRIMARY KEY AUTO_INCREMENT,
d_name VARCHAR(20),
p_id INT REFERENCES t_person(id)
);

实体类关系映射

@Entity
@Table(name = "t_person")
public class Person {@Id@GeneratedValue(strategy = IDENTITY)@Column(name = "id")private Long id;@Column(name = "p_name")private String name;@Column(name = "p_age")private Integer age;@Column(name = "p_email")private String email;@Column(name = "p_birth")private Date birth;@OneToMany(mappedBy="person")private List<Dog> dogs=new ArrayList<>();
}
@Entity
@Table(name = "t_dog")
public class Dog {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "d_id")private Long id;@Column(name = "d_name")private String name;@ManyToOne(targetEntity = Person.class)@JoinColumn(name = "p_id",referencedColumnName = "id")private Person person;
}

3.4映射的注解说明

@OneToMany:

作用:建立一对多的关系映射

属性:

​ targetEntityClass:指定多的多方的类的字节码

​ mappedBy:指定从表实体类中引用主表对象的名称。

​ cascade:指定要使用的级联操作

​ fetch:指定是否采用延迟加载

​ orphanRemoval:是否使用孤儿删除

@ManyToOne

作用:建立多对一的关系

属性:

​ targetEntityClass:指定一的一方实体类字节码

​ cascade:指定要使用的级联操作

​ fetch:指定是否采用延迟加载

​ optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn

作用:用于定义主键字段和外键字段的对应关系。

属性:

​ name:指定外键字段的名称

​ referencedColumnName:指定引用主表的主键字段名称

​ unique:是否唯一。默认值不唯一

​ nullable:是否允许为空。默认值允许。

​ insertable:是否允许插入。默认值允许。

​ updatable:是否允许更新。默认值允许。

​ columnDefinition:列的定义信息。

3.5一对多的操作

1)添加

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {@Autowiredprivate PersonMapper personMapper;@Autowiredprivate DogMapper dogMapper;/*** 测试保存:*      同时保存一个狗主人和一只狗的信息*/@Test@Transactional@Rollback(value = false)public void testSave(){Person person = new Person("张敏",26,"baijie@qq.com",new Date());Dog dog = new Dog();dog.setName("金毛");person.getDogs().add(dog);dog.setPerson(person);personMapper.save(person);dogMapper.save(dog);}
}

2)删除

    /*** 删除操作*/@Testpublic void testDelete(){//删除主表数据,可以直接删除,对应从表外键字段还是删除的主表IDpersonMapper.deleteById(4l);//此时在删除从表会报错dogMapper.deleteById(4l);//如果先删除从表再删除主表则没有问题}

3)级联操作

级联操作:指操作一个对象同时操作它的关联对象

使用方法:只需要在操作主体的注解上配置cascade

@OneToMany(mappedBy="person",cascade = CascadeType.ALL)

cascade:配置级联操作
CascadeType.MERGE 级联更新
CascadeType.PERSIST 级联保存:
CascadeType.REFRESH 级联刷新:
CascadeType.REMOVE 级联删除:
CascadeType.ALL 包含所有

4.jpa中的多对多

4.1.情景模拟

用户和角色:一个用户可以有多个角色,一个角色可以对应多个用户

4.2表关系建立

CREATE TABLE t_role(
r_id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
CREATE TABLE t_person_role(
id INT PRIMARY KEY AUTO_INCREMENT,
r_id INT REFERENCES t_role(r_id),
p_id INT REFERENCES t_person(id)
)

4.3实体关系建立以及映射配置

@Entity
@Table(name = "t_person")
public class Person {@Id@GeneratedValue(strategy = IDENTITY)@Column(name = "id")private Long id;@Column(name = "p_name")private String name;@Column(name = "p_age")private Integer age;@Column(name = "p_email")private String email;@Column(name = "p_birth")private Date birth;@OneToMany(mappedBy="person",cascade = CascadeType.ALL)private List<Dog> dogs=new ArrayList<>();//多对多关系映射@ManyToMany(mappedBy ="persons")private List<Role> roles=new ArrayList<>();}
@Entity
@Table(name = "t_role")
public class Role {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "r_id")private Integer id;@Column(name = "name")private String name;@ManyToMany@JoinTable(name="t_person_role",//中间表的名称joinColumns={@JoinColumn(name="r_id",referencedColumnName="r_id")},inverseJoinColumns={@JoinColumn(name="p_id",referencedColumnName="id")})private List<Person> persons=new ArrayList<>();}

4.4映射的注解说明

@ManyToMany

​ 作用:用于映射多对多关系

​ 属性:

​ cascade:配置级联操作。

​ fetch:配置是否采用延迟加载。

​ targetEntity:配置目标的实体类。映射多对多的时候不用写。

@JoinTable

作用:针对中间表的配置

属性:

​ nam:配置中间表的名称

​ joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段

​ inverseJoinColumn:中间表的外键字段关联对方表的主键字段

@JoinColumn

作用:用于定义主键字段和外键字段的对应关系。

属性:

​ name:指定外键字段的名称

​ referencedColumnName:指定引用主表的主键字段名称

​ unique:是否唯一。默认值不唯一

​ nullable:是否允许为空。默认值允许。

​ insertable:是否允许插入。默认值允许。

​ updatable:是否允许更新。默认值允许。

​ columnDefinition:列的定义信息。

4.5多对多的操作

/*** @author yinhuidong* @createTime 2020-06-04-21:19*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {@Autowiredprivate PersonMapper personMapper;@Autowiredprivate RoleMapper roleMapper;/*** 保存*/@Test@Transactional@Rollback(false)public void test1(){Person person = new Person();person.setName("尹会东");Person person2 = new Person();person2.setName("张贝贝");Role r1 = new Role();r1.setName("学生");Role r2 = new Role();r2.setName("丈夫");person.getRoles().add(r1);person.getRoles().add(r2);person2.getRoles().add(r1);person2.getRoles().add(r2);r1.getPersons().add(person);r2.getPersons().add(person);r1.getPersons().add(person2);r2.getPersons().add(person2);personMapper.save(person);personMapper.save(person2);roleMapper.save(r1);roleMapper.save(r2);}@Test@Transactional@Rollback(false)public void test2(){personMapper.deleteById(10l);}}

5.SpringDataJPA的多表查询

对象导航图查询

对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。

查询一个Person获取它对应的所有Role

    @Transactional@Testpublic void test3(){Person person = personMapper.findById(11l).get();System.out.println("person = " + person);person.getRoles().forEach(System.out::println);}

查询一个角色,获取角色对应的所有person

    @Transactional@Testpublic void test3(){Role role = roleMapper.findById(5l).get();System.out.println("role = " + role);role.getPersons().forEach(System.out::println);}

对象导航查询的问题分析

问题1:我们查询客户时,要不要把联系人查询出来?

分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。

解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。

配置方式:

//LAZY@ManyToMany(mappedBy ="persons",fetch = FetchType.EAGER)

FetchType.EAGER 立即加载

FetchType.LAZY 懒加载

    r1.getPersons().add(person);r2.getPersons().add(person);r1.getPersons().add(person2);r2.getPersons().add(person2);personMapper.save(person);personMapper.save(person2);roleMapper.save(r1);roleMapper.save(r2);}
@Test
@Transactional
@Rollback(false)
public void test2(){personMapper.deleteById(10l);
}

}


## 5.SpringDataJPA的多表查询### 5.1对象导航图查询对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。**查询一个Person获取它对应的所有Role**```java@Transactional@Testpublic void test3(){Person person = personMapper.findById(11l).get();System.out.println("person = " + person);person.getRoles().forEach(System.out::println);}

查询一个角色,获取角色对应的所有person

    @Transactional@Testpublic void test3(){Role role = roleMapper.findById(5l).get();System.out.println("role = " + role);role.getPersons().forEach(System.out::println);}

对象导航查询的问题分析

问题1:我们查询客户时,要不要把联系人查询出来?

分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。

解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。

配置方式:

//LAZY@ManyToMany(mappedBy ="persons",fetch = FetchType.EAGER)

FetchType.EAGER 立即加载

FetchType.LAZY 懒加载

Spring Data Jpa使用篇相关推荐

  1. 【SpringBoot框架篇】11.Spring Data Jpa实战

    文章目录 1.简介 1.1.JPA 1.2.Spring Data Jpa 1.3.Hibernate 1.4.Jpa.Spring Data Jpa.Hibernate三者之间的关系 2.引入依赖 ...

  2. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

  3. SpringBoot 实战 (八) | 使用 Spring Data JPA 访问 Mysql 数据库

    微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 如题,今天介绍 Spring Data JPA 的使用. 什么是 Spring Data JPA 在介绍 Spri ...

  4. Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 上一篇我们介绍了在使用JdbcTemplate来做数据访 ...

  5. Spring Boot 2.x基础教程:使用Spring Data JPA访问MySQL

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 翟永超 来源 | http://blog.di ...

  6. 使用Spring Data JPA进行分页和排序

    通过代码示例学习使用Spring Data JPA进行分页和排序 . 了解如何使用Springs PagingAndSortingRepository接口获取分页和排序结果. 1概述 在处理大量数据时 ...

  7. 从Spring Data JPA访问EntityManager

    Spring Data JPA允许您通过使用Repository接口来快速开发数据访问层. 有时,您需要从Spring Data JPA访问EntityManager. 这篇文章向您展示了如何访问En ...

  8. Spring Data JPA教程:获取所需的依赖关系

    在创建使用Spring Data JPA的应用程序之前,我们需要获取所需的依赖关系. 这篇博客文章标识了必需的组件,并描述了如何使用Maven获得它们. 让我们开始吧. 其他阅读:如果您不熟悉Spri ...

  9. Spring Data JPA教程:简介

    创建使用Java Persistence API的存储库是一个繁琐的过程,需要大量时间,并且需要大量样板代码. 通过执行以下步骤,我们可以消除一些样板代码: 创建一个抽象的基础存储库类,该类为实体提供 ...

  10. 使用Spring Data REST将Spring Data JPA存储库导出为REST服务

    Spring Data模块提供了各种模块,以统一的方式处理各种类型的数据源,如RDBMS,NOSQL存储等. 在我以前的文章SpringMVC4 + Spring Data JPA +使用JavaCo ...

最新文章

  1. Activiti 笔记
  2. sqlite--代码操作
  3. 渗透知识-SQL注入
  4. 进制转换,字符串,字节串之间转换
  5. 关于猿如何找对象,心里没点那啥数吗?
  6. 拯救普通人周报焦虑,一个自动化报表工具就能实现
  7. 在手机上写python_牛逼啊!一个随时随地写Python代码的神器
  8. 进阶之路:Java 日志框架全画传(上)
  9. 在线去水印网站_一键去水印工具
  10. 线性代数之n维向量-思维导图
  11. 压缩文件下载后无法打开问题解决方法
  12. 圆角正方形 html,ps正方形角怎么变圆角 ps怎么在原来的矩形中改成圆角
  13. 计算机D盘无法读取,D盘目录或文件被损坏且无法读取的愿意以及解决办法
  14. 全球十大智能物流装备龙头企业
  15. 【2019-TGRS】Aerial LaneNet: Lane-Marking Semantic Segmentation in Aerial Imagery Using Wavelet-Enhanc
  16. 《Linux系统管理与应用》课程知识点整理+书后习题全文解答(Linux知识点大纲)
  17. Windows 窗口发送消息参数详解
  18. 在2B和2C之间,还有一个2H
  19. 典型环节的matlab仿真分析,实验一 典型环节的MATLAB仿真
  20. 来自一位程序员的经验之谈(三)

热门文章

  1. 淘宝无线端一键直达微博自助生成工具
  2. “一键淘宝”将淘宝网店免费转移到手机客户端教程
  3. FIT2CLOUD安装
  4. 计算机函数求销售额公式,excel用函数求出销售额 使用TREND函数来对销售额进行预测的方法...
  5. ElasticSearch 多字段分组求和
  6. 前端实现下载文件-js实现a标签下载
  7. BET365的websocket实时数据采集分析
  8. 计算机网络实验2-二层交换机的基本配置(eNSP)
  9. 邱锡鹏神经网络怎么样,邱锡鹏神经网络答案
  10. [强化学习实战]深度Q学习-DQN算法原理