笔记


1.什么是jpa

 

  JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

 

持久化:

      何谓"持久化" 持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中

 

2.为什么要学习jpa

 

优势:

标准化

JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

容器级特性的支持

JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

 

简单方便

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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

小结:

EJB 3.0和JPA 毫无疑问将是Java EE 5的主要卖点。在某些领域中,它们给Java社区带来了竞争优势,并使Java 在其他领域与竞争对手不分伯仲(因为,不可否认,某些领域尚不存在基于标准的方法)。

过去数年来,Spring Framework一直是EJB在企业领域的主要竞争对手。EJB 3.0规范解决了很多促进Spring兴起的问题。随着它的出现,EJB3.0毫无疑问比Spring提供了更好的开发体验--最引人注目的优势是它不需要配置文件

JPA提供一种标准的OR映射解决方案,该解决方案完全集成到EJB3.0兼容的容器中。JPA的前辈将会继续稳定发展,但是业务应用程序中的 raw 使用将可能会减少。实现 JPA 兼容的实体管理器似乎很可能是此类技术的发展方向。

Java EE系列规范的较大问题与JPA没有任何关系。Java EE 系列规范的问题涉及到 Web和EJB容器之间的集成。Spring在此领域仍然具有主要竞争优势。JBoss的Seam项目尝试使用自定义的方法来解决这一问题。Caucho Resin应用服务器试图扩展容器边界并支持在Web容器中使用@EJB注释。我们希望Java EE 5.1将解决层集成的问题,为我们提供一个全面而标准的依赖性注入方法。

在不久的将来,Oracle可能会将JPA作为一个单独的JSR对待,同时JPA还可能作为Java SE的一部分。不过这些都不太重要,重要的是,我们已经可以在脱离容器的情况下、在Java SE应用中使用JPA了。

JPA已经作为一项对象持久化的标准,不但可以获得Java EE应用服务器的支持,还可以直接在Java SE中使用。开发者将无需在现有多种ORM框架中艰难地选择,按照Sun的预想,现有ORM框架头顶的光环将渐渐暗淡,不再具有以往的吸引力。

jpa和hibernate

  JPA本身是一种规范,它的本质是一种ORM规范(不是ORM框架,因为JPA并未提供ORM实现,只是制定了规范)因为JPA是一种规范,所以,只是提供了一些相关的接口,但是接口并不能直接使用,JPA底层需要某种JPA实现,JPA现在就是Hibernate功能的一个子集

  JPA和Hibernate之间的关系,可以简单的理解为JPA是标准接口,Hibernate是实现,并不是对标关系,借用下图可以看清楚他们之间的关系,Hibernate属于遵循JPA规范的一种实现,但是JPA是Hibernate遵循的规范之一,Hibernate还有其他实现的规范

 

 

 

3.如何使用jpa->快速入门

 1)创建maven项目导入依赖

<dependencies>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>javax.el</artifactId>
        <version>2.2.6</version>
    </dependency>
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>2.2.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>2.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.12</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.4</version>
    </dependency>
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.1</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.4.21.Final</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

 

2)编写xml配置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.dataSource 配置数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa" />
        <property name="user" value="root" />
        <property name="password" value="root" />
    </bean>


    <!--2.配置entityManagerFactory-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置[实体类]扫描包-->
        <property name="packagesToScan" value="com.gg.domain"/>
        <!--配置jpa实现厂家-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>


        <!--3.配置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"/>


            </bean>
        </property>

        <!--jpa的方言,高级的特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
    </bean>


    <!--4.整合spring data jpa-->
                        <!--[dao]扫描包-->
    <jpa:repositories base-package="com.gg.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory">
    </jpa:repositories>


    <!--5.配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!--6.声明式事务-->

    <!--7.配置spring注解扫描包-->
    <context:component-scan base-package="com.gg"/>

</beans>

 

3)编写实体类与实体类映射关系

@Entity
@Table(name = "cst_customer")
public class Customer {
略

 

4)编写jpa的dao层接口

  • 需要继承两个接口(JpaRepository,JpaSpecificationExecutor)
  • 需要提供响应的泛型
/**
* 符合SpringDataJpa的dao层接口规范
* JpaRepository <操作的实体类类型,实体类中主键属性的类型>
*     封装了基本的CRUD操作
* JpaSpecificationExecutor<操作的实体类类型>
*     封装了复杂查询(分页等..)
*/
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
    //这里什么都不用写,就可以完成基本的CRUD操作
}

 

5)测试

/**
* 根据id查询
*/
@Test
public void test1(){
    Customer customer = customerDao.findById(3L).get();
    System.out.println(customer);
}

 

6)增删查改

@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml") //指定spring容器的配置信息
public class CustomerDaoTest {
    @Autowired
    private CustomerDao customerDao;

    /**
     * 根据id查询
     */
    @Test
    public void test1(){
        Customer customer = customerDao.findById(3L).get();
        System.out.println(customer);
    }

    /**
     * save方法:存在记录就更新
     */
    @Test
    public void test2(){

        Customer customer = new Customer();
        customer.setCustId(1L);
        customer.setCustName("700");
        customer.setCustAddress("000");
        customer.setCustSource("000");


        Customer save = customerDao.save(customer);
        System.out.println(save);
    }

    /**
     * save方法:不存在记录就添加
     */
    @Test
    public void test3(){

        Customer customer = new Customer();
        customer.setCustId(4L);
        customer.setCustName("700");
        customer.setCustAddress("000");
        customer.setCustSource("000");


        Customer save = customerDao.save(customer);
        System.out.println(save);
    }

    /**
     * 删除
     */
    @Test
    public void test4(){
        customerDao.deleteById(5L);
    }

 

spring-data Jpa执行过程

接口

真正发挥作用:

    接口的实现类

在程序执行的过程中,自动的帮助我们动态生成了接口的实现类的接口

如果动态的生成实现类对象

    动态代理(生成基于接口的实现类的对象)

 

流程

customerDao.findById(3L).get()
1.通过JDKDynamicAopProxy创建动态代理对象
2.动态代理对象:simpleJpaRepository    实现了JpaRepository接口,实现了JpaSpecificationExecutor接口
3.findOne():通过entityManager完成查询操作


public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

声明对此包下的dao接口进行动态代理增强!
<jpa:repositories base-package="com.gg.dao" transaction-manager-ref="transactionManager"
                  entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>

 

 

spring-data jpa 高级查询

 接口原方式

1.查询总数量

/**
* 测试统计查询:查询客户的总数量
*/
@Test
public void test6(){
    long count = customerDao.count();   //查询全部的客户数量
    System.out.println(count);
}

 

2.判断id为4的客户是否存在

/**
* 判断id为4的客户是否存在
*      1.可以查询一下id为4的客户
*        如果值为空,代表不存在,如果不为空,代表存在
*      2.判断数据库中id为4的客户的数量
*        如果数量为0,代表不存在,如果大于0,代表存在
*/
@Test
public void test7(){
    boolean exists = customerDao.existsById(4L);
    System.out.println(exists);
}

 

3.根据id从数据库查询

/**
* 根据id从数据库查询
*/
@Test
@Transactional  //保证getOne正常运行
public void test8(){
    Customer customer = customerDao.getOne(3L);
    System.out.println(customer);
}

 

jpql查询方式

 1.使用jpql查询用户

public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
    //这里什么都不用写,就可以完成基本的CRUD操作


    /**
     * 案例:根据客户名称查询客户
     *      使用jpql的形式查询
     *      jpql:from Customer where custName = ?
     *
     *
     *      配置jpql语句,使用的@Query注解
     */


    @Query(value = "from Customer where custName = ?1")
    public Customer findJpql(String custName);

@Test
public void testFindJPQL(){
    Customer customer = customerDao.findJpql("可可");
    System.out.println(customer);
}

 

 2.根据客户名称和客户id查询客户

/**
* 案例:根据客户名称和客户id查询客户
* jpql:from Customer where custName = ? and custId = ?
*
* 对于多个占位符参数
*      赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
*/
@Query(value = "from Customer where custName = ?1 and custId = ?2")
public Customer findCustNameAndId(String custName,Long Id);


@Test
public void testFindJPQL1(){
    Customer customer = customerDao.findCustNameAndId("700",1L);
    System.out.println(customer);
}


* 可以指定占位符参数的位置
*/
@Query(value = "from Customer where custId = ?2 and custName = ?1")
public Customer findCustNameAndId(String custName,Long Id);

 

3.!!!  使用jpql完成更新操作    !!!

/**
* 使用jpql完成更新操作
*      案例:根据id更新,客户的名称
*           更新4号客户的名称
*  sql:update cst_customer set cust_name = ? where cust_id = ?
*  jpql: update Customer set custName = ? where custId = ?
*
*  @Query:代表的是进行查询
*      * 声明此方法是用来进行更新操作
*  @Modifying
*      * 当前执行的是一个更新操作
*/
@Query(value = "update Customer set custName = ?2 where custId = ?1")
@Modifying
void updateCustomer(Long custId,String CustName);


/**
* 测试jpql的更新操作
* * springDataJpa中使用jpql完成 更新/删除操作
*      * 需要手动添加事务的支持
*      * 默认会执行结束之后,回滚事务
*  @Rollback设置是否自动回滚
*      false true
*/
@Test
@Transactional  //添加事务支持
@Rollback(value = false)    默认会自动回滚,需要取消回滚
public void testFindJPQL2(){
    customerDao.updateCustomer(1L,"007");
}

 

 4.SQL查询

/**
* sql语句的查询
*      1.特有的查询:需要在dao接口上配置方法
*      2.在新添加的方法上,使用注解的形式配置sql查询语句
*      3.注解:@Query
*          value:jpql语句 | sql语句
*          nativeQuery : false(使用jpql查询) | true(使用本地查询:sql查询)
*              是否使用本地查询
*/


// 使用sql的形式查询
// 查询全部的客户
//sql: select * from cst_customer
@Query(value = "select * from cst_customer",nativeQuery = true)
List<Object []> findSql();


@Test
public void test1(){
    /*
    Hibernate: select * from cst_customer
    [1, 000, null, null, 007, null, 000]
    [2, 11, null, null, gg, null, 213]
    [3, 001, 乱码, null, 可可, null, 213]
    [4, 001, 乱码, null, 可惜, null, 213]
     */
    List<Object[]> objects = customerDao.findSql();
    for (Object[] object : objects) {
        System.out.println(Arrays.toString(object));
    }

}

 

5.模糊查询

/**
* sql模糊查询
*/
@Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true)
List<Customer> findLikeCustomer(String custName);


@Test
public void test2(){
    List<Customer> customerList = customerDao.findLikeCustomer("可%");
    customerList.forEach(System.out::println);
}

 

测试结果:

Hibernate: select * from cst_customer where cust_name like ?
Customer{custId=3, custName='可可', custSource='213', custIndustry='乱码', custLevel='null', custAddress='001', custPhone='null'}
Customer{custId=4, custName='可惜', custSource='213', custIndustry='乱码', custLevel='null', custAddress='001', custPhone='null'}

 

方法名称规则查询

  • 是对jpql查询更加深层的一层封装,我们只需要按照SpringData提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询

_1.精准查询

/**
*  方法名的约定
*      findBy:查询
*          对象中的属性名(首字母大写),查询的条件
*   findByCustName -> 根据客户名称查询
*   在springdataJpa的运行阶段
*      会根据方法名称进行解析 findBy from xxx(实体类)
*                              属性名称  where  custName = ?
*/
Customer findByCustName(String custName);


@Test
public void test3(){
    Customer customer = customerDao.findByCustName("可可");
    System.out.println(customer);
}

 

_2.单条件查询

/**
*  方法名的约定
*      findBy:查询
*          对象中的属性名(首字母大写),查询的条件
*          CustName
*          * 默认情况 : 使用等于的方式查询
*              特殊的查询方式
*
*   findByCustName -> 根据客户名称查询
*   在springdataJpa的运行阶段
*      会根据方法名称进行解析 findBy from xxx(实体类)
*                              属性名称  where  custName = ?
*
*
*    findBy + 属性名称 (根据属性名称进行完成匹配的查询)
*      findBy + 属性名称 + "查询方式(Like | isnull)"
*          findByCustNameLike
*/


List<Customer> findByCustNameLike(String name);

@Test
public void test4(){
    List<Customer> list = customerDao.findByCustNameLike("可%");
    list.forEach(System.out::println);
}

 

_3.多条件查询

*    3.多条件查询
*    findBy + 属性名 + "查询方式" + "多条件的连接符(AND | OR)" + 属性名 + "查询方式"

/**
* 使用客户名称模糊匹配
*/
Customer findByCustNameLikeAndCustSource(String name,String source);

@Test
public void test5(){
    Customer customer = customerDao.findByCustNameLikeAndCustSource("%可", "213");
    System.out.println("customer = " + customer);
}

 

Specifications动态查询

Specification:查询条件

  自定义我们自己的Specification实现类

    实现

    //root:查询的根对象(查询的任何属性都可以从根对象中获取)

    //CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)

    //CriteriaBuilder:查询的构造器,封装了很多的查询条件

    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,CriteriaBuilder cb)    //封装查询条件

 

1)自定义查询,单条件精准查询

/**
* 根据条件:查询单个对象
*/
@Test
public void test(){


    /**
     * 自定义查询条件
     *      1.实现Specification接口(提供泛型,查询的对象类型)
     *      2.实现toPredicate方法(构造查询条件)
     *      3.需要借助方法参数中的两个参数
     *          root:获取需要查询的对象属性
     *          CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
     *      案例:根据客户名称查询,查询客户名为xxx的客户
     *          查询条件
     *              1.查询方式
     *                  cb对象
     *              2.比较的属性名称
     *                  root对象
     *              3.
     */
    Specification<Customer> spec = new Specification<Customer>() {
        /**
         * 匿名内部类
         * @param root
         * @param query
         * @param criteriaBuilder
         * @return
         */
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            // 1.获取比较的属性
            Path<Object> custName = root.get("custName");
            // 2.构造查询   select * from cst_customer where cust_name = 'xxx'
            // criteriaBuilder.equal(需要比较的属性,比较的取值)
            Predicate predicate = criteriaBuilder.equal(custName, "可惜");// 进行精准匹配(比较的属性名,比较的属性的取值)


            return predicate;
        }
    };


    Optional<Customer> customer = customerDao.findOne(spec);
    System.out.println(customer);
}

 

2)多条件查询

/**
* 多条件查询
*      案例:根据客户名称和客户xxx查询
*/
@Test
public void test1(){
    /**
     * root:获取属性
     *      客户名
     *      其他
     * cb:构造查询
     *      1.构造客户名的精准匹配查询
     *      2.构造其他属性的精准匹配查询
     *      3.将以上两个查询联系起来
     */
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {


            Path<Object> custName = root.get("custName");
            Path<Object> custSource = root.get("custSource");
            // 查询条件1
            Predicate predicate1 = criteriaBuilder.equal(custName, "007");
            // 查询条件2
            Predicate predicate2 = criteriaBuilder.equal(custSource, "000");
            // 将多个查询条件组合(满足条件1和条件2,与关系 / 满足条件1或满足条件2,或关系)
            Predicate resPredicate = criteriaBuilder.and(predicate1, predicate2);


            return resPredicate;
        }
    };
    Optional<Customer> customer = customerDao.findOne(spec);
    System.out.println(customer);
}

 

3)模糊查询,定义查询字段的类型

/**
* 案例:完成根据客户名称的模糊匹配,返回客户列表
*
* equal : 直接得到path(属性),然后进行比较即可
* gt lt le like : 得到path对象,根据path指定比较的参数类型,再去进行比较
*      指定参数类型:path.as(类型的字节码对象)
*/
@Test
public void test3(){


    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {


            Path<Object> custName = root.get("custName");
            Predicate predicate = criteriaBuilder.like(custName.as(String.class),"可%");
            return predicate;


        }
    };
    List<Customer> list = customerDao.findAll(spec);
    list.forEach(System.out::println);


}

 

4)添加排序

/**
* 添加排序规则
*/
@Test
public void test4(){


    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {


            Path<Object> custName = root.get("custName");
            Predicate predicate = criteriaBuilder.like(custName.as(String.class),"可%");
            return predicate;


        }
    };


    // 添加排序
    // 创建排序对象
    // 第一个参数:排序的顺序(倒序,正序)
    // 第二个参数:排序的属性名称
    Sort sort = Sort.by(Sort.Direction.DESC, "custId");
    List<Customer> list = customerDao.findAll(spec,sort);
    list.forEach(System.out::println);


}

 

5) 分页

/**
     * 分页查询
     * findAll(Specification,Pageable):带有条件的分页
     *  findAll(Pageable):没有条件的分页
     *
     *  返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,总条数)
     */
    @Test
    public void test5(){


        /**
         * PageRequest对象是Pageable接口的实现类
         *
         * 创建PageRequest的过程中,需要调用它的构造方法传入两个参数
         *      第一个参数:当前查询的页数
         *      第二个参数:每页查询的数量
         */


        Pageable pageable = PageRequest.of(1, 3);
        Specification spec = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) {
                return null;
            }
        };
        Page<Customer> customerPage = customerDao.findAll(spec,pageable);
        System.out.println("customerPage.getTotalElements() = " + customerPage.getTotalElements());
        System.out.println("customerPage.getTotalPages() = " + customerPage.getTotalPages());
//        System.out.println("customerPage.getContent() = " + customerPage.getContent());
        System.out.println("customerPage.getNumber() = " + customerPage.getNumber());
        System.out.println("customerPage.getNumberOfElements() = " + customerPage.getNumberOfElements());
        System.out.println("customerPage.getPageable() = " + customerPage.getPageable());
//        customerPage.forEach(System.out::println);
        System.out.println("customerPage.nextPageable() = " + customerPage.nextPageable());


    }

 

多表之间的关系和操作多表的操作步骤

 

完成多表操作

  • 一对多查询

添加表对应实体类

@Entity
@Table(name = "cst_linkman")
public class LinkMan {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "lkm_id")
    private Long lkmId;
    @Column(name = "lkm_name")
    private String lkmName;
    @Column(name = "lkm_gender")
    private String lkmGender;
    @Column(name = "lkm_phone")
    private String lkmPhone;
    @Column(name = "lkm_mobile")
    private String lkmMobile;
    @Column(name = "lkm_email")
    private String lkmEmail;
    @Column(name = "lkm_position")
    private String lkmPosition;
    @Column(name = "lkm_memo")
    private String lkmMemo;


    /*配置联系人到客户的多对一关系
        使用注解的形式配置多对一关系
            1.配置表关系
            2.配置外键(中间表)
    */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;

 

添加表关系

/**
* 使用注解的形式配置多表关系
*      1.声明关系
*          @OneToMany(targetEntity = LinkMan.class)
*      2.配置外键(中间表)
*          @JoinColumn:配置外键
*              name:外键字段名称
*              referencedColumnName:参照的主表的主键字段名称
*
*
*      * 在客户实体类上(一方)添加了外键的配置,所以对于客户而言,也具备了维护外键 作用
*/
// 配置客户和联系人之间的关系(一对多关系)
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<>();

 

测试

@Autowired
    private LinkManDao linkManDao;

    @Autowired
    private CustomerDao customerDao;

    @Test
    @Transactional
    @Rollback(value = false)
    public void test(){
        Customer customer = new Customer();
        customer.setCustName("西西");


        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("米米");
        linkMan.setCustomer(customer);


        customerDao.save(customer);
        linkManDao.save(linkMan);


    }

    @Test
    @Transactional
    @Rollback(value = false)
    public void test1(){
        Customer customer = new Customer();
        customer.setCustName("西西1");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("米米1");

//        linkMan.setCustomer(customer);
        List<LinkMan> list = new ArrayList<LinkMan>();
        list.add(linkMan);
        customer.setLinkMans(list);
        linkMan.setCustomer(customer);

        customerDao.save(customer);
        linkManDao.save(linkMan);

    }

 

放弃外键保护

//    @OneToMany(targetEntity = LinkMan.class)
//    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")


    /**
     * 放弃外键维护权
     *      mapperBy:对方配置关系的属性名称
     */
    @OneToMany(mappedBy = "customer")
    private List<LinkMan> linkMans;

 

一对多查询(获取类联系中的所属集合元素) 使用延迟加载

@Test
@Transactional
@Rollback(value = false)
public void test4(){
    Customer customer = customerDao.getOne(96L);
    List<LinkMan> linkMans = customer.getLinkMans();
    for (LinkMan linkMan : linkMans) {
        System.out.println("linkMan = " + linkMan);
    }
}

 

延迟加载:FetchType

*      fetch:配置关联对象的加载方式
*          FetchType.EAGER:立即加载
*          FetchType.LAZY:延迟加载
*/
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)

 

多对一查询(获取联系集合属于哪一个类)    使用立即加载

@Test
@Transactional
@Rollback(value = false)
public void test5(){
    LinkMan linkMan = linkManDao.getOne(5L);
    Customer customer = linkMan.getCustomer();
    System.out.println("customer = " + customer);

}

 

级联操作配置

 

级联添加

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private List<LinkMan> linkMans;


/**
*  级联添加:保存一个客户的同时,保存客户的所有联系人
*      需要在操作主题的实体类上,配置cascade属性
*/
@Test
@Transactional
@Rollback(value = false)
public void test2(){
    Customer customer = new Customer();
    customer.setCustName("111");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("2221");

    linkMan.setCustomer(customer);

    List<LinkMan> list = new ArrayList<>();
    list.add(linkMan);
    customer.setLinkMans(list);

    customerDao.save(customer);

}

 

级联删除

/**
* 级联删除
*      删除1号客户的同时,删除1号客户的所有联系人
*/
@Test
@Transactional
@Rollback(value = false)
public void test3(){
    customerDao.deleteById(94L);
}

 

级联的其他参数

* cascade:配置级联
*      CascadeType.ALL     : 所有
*                  MERGE   :更新
*                  PERSIST :保存
*                  REMOVE  :删除
*/
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private List<LinkMan> linkMans;

 

  • 多对多查询
/**
     * 配置多对多
     */
//    @ManyToMany(targetEntity = User.class)  // 配置多表关系
//    @JoinTable(name = "sys_user_role",  // 中间表
//            // 当前对象在中间表中的外键
//            joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")},
//            // inverseJoinColumns,对方对象在中间表的外键
//            inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}
//    )


    @ManyToMany(mappedBy = "roles",cascade = CascadeType.ALL)
    private Set<User> users = new HashSet<>();


/**
* 配置用户到角色的多对多关系
*      配置多对多映射关系
*          1.声明表关系的配置
*              @ManyToMany(targetEntity = Role.class) 配置多对多
*              targetEntity:代表对方的实体类字节码
*          2.配置中间表(包含两个外键)
*              @JoinTable:中间表的名称
*              joinColumns:配置当前对象在中间表的外键
*                  @JoinColumn数组
*                      name:外键名
*                      referencedColumnName:参照的主表的主键名
*                  inverseJoinColumns:配置对方对象在中间表的外键
*
*/
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "sys_user_role",
        // 当前对象在中间表中的外键
        joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
        // inverseJoinColumns,对方对象在中间表的外键
        inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<>();

 

测试

@Test
    @Transactional
    @Rollback(value = false)
    public void test1(){
        User user = new User();
        user.setUserName("小李");

        Role role = new Role();
        role.setRoleName("前端工程师");

//        role.getUsers().add(user);
        user.getRoles().add(role);

        userDao.save(user);
        roleDao.save(role);

    }



@Test
    @Transactional
    @Rollback(value = false)
    public void test2(){
//        Optional<User> user = userDao.findById(1L);
        userDao.deleteById(1L);
    }

 

时间花在哪里,成就就在哪里