jpa02

springdata jpa

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

 

quick start

 

pom.xml

<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>${spring.version}</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>

 

springdata jpa 整合 spring

 

applicationContext.xml

可以通过 @ContextConfiguration(locations = "classpath: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">


    <!--1.创建entityManagerFactory对象交给spring容器管理 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>

        <property name="packagesToScan" value="com.jpa.domain"/>

        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <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"/>
                <property name="showSql" value="true"/>
            </bean>
        </property>


        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>

    </bean>

    <!--创建数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="jdbcUrl" value="jdbc:mysql:///jpa"/>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
     </bean>

    <!--整合spring dataJpa-->
    <jpa:repositories base-package="com.jpa.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>


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

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

    <!--6.配置包扫描-->
    <context:component-scan base-package="com.jpa"></context:component-scan>
</beans>

1. 创建实体类 

 { 1.实体与表的映射关系,2.类中属性和表中字段的映射关系 }

2.dao层的接口规范

  需要继承两个接口(JpaRepository,JpaSpecificationExecutor)

  提供响应的泛型

extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>

  JpaRepository<操作的实体类类型,实体类中主键属性的类型>

  JpaSpecificationExecutor<操作的实体类类型>

save 保存或更新,如果传递的对象是否有主键id,如果没有主键属性,保存

存在主键属性,根据id查询数据,更新数据

 

运行原理

1.通过jdkDynamicAopProxy的invoke方法创建一个动态代理对象

2.SimpleJpaRepository当中封装了jpa的操作

3.通过hibernate完成数据库操作

 

复杂查询

1.使用接口定义的方法进行查询

2.jpql 的查询方式

  查询的是类和类中的属性

需要将jpql 语句配置到接口方法上,在新添加的方法上,使用注解形式配置jpql查询语句,注解@Query

 

根据客户名称查询客户
1     /**
2      * 使用jpql查询
3      *      from Customer where custName = ?
4      *      @Query注解
5      *          value: sql语句 | jpql语句
6      *          nativeQuery = true,使用sql ,false(默认),使用jpql
7      */
8     @Query(value = " from Customer where custName = ?")
9     public Customer findJpql(String custName);


根据客户名称和客户id查询客户
 1 /**
 2      *      jpql: from Customer where custName = ? and custId = ?
 3      *                                                  参数与条件不存在关系
 4      *      对于多个占位符参数,赋值的时候默认的情况
 5      *      占位符的位置需要和方法参数保持一致
 6      *      如果不一致,可以指定占位符参数的位置
 7      *      ? 索引的方式 ,指定此占位的取值来源
 8      *
 9      */
10     @Query(value = "from Customer where custName = ? and custId = ?")
11     public Customer findCustNameAndId(String name ,Long id);

 

更新客户

1    /**u/d
2      * sql: update cst_customer set cust_name = ? where cust_id = ?
3      * @Moditying
4      *          表示当前执行的是更新操作
5      * 需要手动添加事务,默认执行后会回滚 @Transaction
6      */
7     @Query(value = "update Customer set custName = ?2 where custId = ?1")
8     @Modifying
9     public void updateCustomer(long custId,String custName);

 

3.方法命名规则查询

按照SpringDataJpa 提供的方法名称规则定义方法,不需要配置jpql语句

findBy 查询

  根据属性名称进行查询

  对象中的属性名(首字母大写),查询的条件

 public Customer findByCustName(String custName);

findBy  属性名称(根据属性名称进行匹配的查询)

findBy 属性名称 查询方式(like | isnull )

 public Customer findByCustNameLike(String custName);

多条件查询

findBy 属性名- 查询方式- 多条件的连接符(or | and)-属性名-查询方式

 public Customer findByCustNameAndCustIndustry(String custName,String custIndustry);

 

 

动态查询

Specifications

1 public interface Specification<T> {
2     Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
3 }

 

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

实现方法中参数
  root:查询的根对象(查询的任何属性都可以从根对象中获取)
  CriteriaQuery:顶层查询对象,自定义查询方式(一般不用)
  CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); //封装查询条件

 

 1 /**
 2      * 自定义查询条件
 3      *  1.实现Specification 接口(提供泛型,查询对象类型)
 4      *  2.实现toPredicate方法(构造查询条件)
 5      *  3.需要借助方法参数中的两个参数
 6      *      root 需要查询的对象属性
 7      *      CriteriaBuilder 构造查询条件,内部封住了查询条件(模糊匹配,精确匹配)
 8      */
 9     @Test
10     public void testSpec(){
11 
12         Specification<Customer> spec = new Specification<Customer>() {
13             public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
14 
15                 Path<Object> custName = root.get("custName");
16                 Predicate predicate = cb.equal(custName, "zhangsan");//equal 进行精准匹配 (属性,取值)
17                 return predicate;
18             }
19         };
20 
21         Customer customer = customerDao.findOne(spec);
22         System.out.println(customer);
23     }

 

多条件拼接

 1     /**
 2      * 多条件查询
 3      * 根据客户名和行业查询
 4          * root用来获取属性
 5          * cb构造查询
 6      *      1.构造客户名的精准匹配查询
 7      *      2.构造所属行业的精准匹配查询
 8      *      3.将连个查询联合起来
 9      */
10     @Test
11     public void testSpec1(){
12         Specification<Customer> spec = new Specification<Customer>() {
13             public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
14                 Path<Object> custName = root.get("custName");
15                 Path<Object> custIndustry = root.get("custIndustry");
16 
17                 //构造查询
18                 Predicate p1 = cb.equal(custName, "zhangsan");
19                 Predicate p2 = cb.equal(custIndustry, "it");
20                 //将查询条件组合起来
21                 Predicate and = cb.and(p1, p2);
22                 return and;
23             }
24         };
25 
26         Customer customer = customerDao.findOne(spec);
27         System.out.println(customer);
28     }

 

模糊匹配及排序

 1  /**
 2      * 模糊匹配
 3      * equal 直接得到path对象
 4      * gt,lt,le ,like 得到path对象,根据path指定的参数类型,进行比较
 5      * 指定参数类型 : path.as(类型的字节码对象)
 6      */
 7     @Test
 8     public void testSpec2(){
 9         Specification<Customer> spec = new Specification<Customer>() {
10             public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
11                 //查询属性客户名
12                 Path<Object> custName = root.get("custName");
13                 //查询方式模糊匹配
14                 Predicate like = cb.like(custName.as(String.class), "华%");
15 
16                 return like;
17             }
18         };
19 
20 //        List<Customer> list = customerDao.findAll(spec);
21 //        for (Customer customer : list){
22 //            System.out.println(customer);
23 //        }
24 
25     //添加排序
26     //创建排序对象,需要调用构造方法实例化sort对象
27     //第一个参数:排序的顺序
28         //Sort.Direction.Desc:倒序
29         //Sort.Direction.ASC:升序
30     //第二个参数:排序的属性名称
31         Sort sort  = new Sort(Sort.Direction.DESC,"custId");
32         List<Customer> list = customerDao.findAll(spec,sort);
33         for (Customer customer : list) {
34             System.out.println(customer);
35         }
36 
37 
38     }

 

分页

 1     /**
 2      * 分页查询
 3      *  findAll(Specification,Pageable)
 4      *  Specficaation 查询条件
 5      *  Pageable 分页参数
 6      *
 7      */
 8     @Test
 9     public void testSpec4(){
10 
11         Specification spec = null;
12         Pageable pageable = new PageRequest(0, 2); //当前查询页数,每页查询条数
13         Page<Customer> page = customerDao.findAll(null, pageable);
14         System.out.println(page.getTotalPages());
15         System.out.println(page.getTotalElements());
16 
17     }

 

 

 

多表操作

 

多表关系


一对多:
一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键


多对多:
中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键

 

 

分析步骤

1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,再实体类中描述表关系(包含关系)
4.配置映射关系

 

一对多

客户:联系人   1:*

联系人: 公司  1:1

客户与联系人   (一对多) 

 

Customer

 1     /**
 2      * 客户与联系人,一对多
 3      * 使用注解的形式配置多表关系
 4      * 1.声明关系 @OnetwoMany targetEntity: 对方对象的字节码对象
 5      * 2.配置外键(中间表) name 外键字段名称
 6      *                  referencedColumnName 参照主表的主键字段名称
 7      *
 8      *  ! 在客户实体类添加了外键设置,对客户而言,具备了维护主键的作用
 9      */
10     @OneToMany(targetEntity = LinkMan.class)
11     @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
12     private Set<LinkMan> linkMans = new HashSet();

 

LinkMan

 1     /**
 2      * 配置联系人到客户的多对一关系
 3      *      使用注解的方式配置多对一关系
 4      *      1.配置表关系  @ManytoOne targetTarget 对方实体类的字节码
 5      *      2.配置外键(中间表)
 6      *
 7      *  配置外键的过程,配置到了多了一方,就会在多的一方维护外键
 8      */
 9     @ManyToOne(targetEntity = Customer.class)
10     @JoinColumn(name ="lkm_cust_id",referencedColumnName = "cust_id")
11     private Customer customer;

 

applicationContext.xml

entityManagerFactory 配置 property
 1         <!--注入jpa的配置信息
 2             加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息
 3             hibernate.hbm2ddl.auto : 自动创建数据库表
 4                 create : 每次都会重新创建数据库表
 5                 update:有表不会重新创建,没有表会重新创建表
 6         -->
 7         <property name="jpaProperties" >
 8             <props>
 9                 <prop key="hibernate.hbm2ddl.auto">update</prop>
10             </props>
11         </property>

进行保存

 1     @Test
 2     @Transactional
 3     @Rollback(false)
 4     public void  add(){
 5         Customer customer = new Customer();
 6         customer.setCustName("zhangsan");
 7         LinkMan linkMan = new LinkMan();
 8         linkMan.setLkmName("lisi");
 9 
10         customer.getLinkMans().add(linkMan);
11 
12         customerDao.save(customer);
13         linkManDao.save(linkMan);
14     }

 

放弃外键维护( 一的一方)

Customer

 1  /**
 2      * 放弃外键维护权
 3      *      mappedBy:对方配置关系的属性名称\
 4      * cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
 5      *      CascadeType.all         : 所有
 6      *                  MERGE       :更新
 7      *                  PERSIST     :保存
 8      *                  REMOVE      :删除
 9      *
10      * fetch : 配置关联对象的加载方式
11      *          EAGER   :立即加载
12      *          LAZY    :延迟加载
13 
14      */
15     @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
16     private Set<LinkMan> linkMans = new HashSet<>();

 

一对多级联添加

 1     /**
 2      * 级联添加:保存一个客户的同时,保存客户的所有联系人
 3      *      需要在操作主体的实体类上,配置casacde属性
 4      */
 5     @Test
 6     @Transactional //配置事务
 7     @Rollback(false) //不自动回滚
 8     public void testCascadeAdd() {
 9         Customer customer = new Customer();
10         customer.setCustName("百度1");
11 
12         LinkMan linkMan = new LinkMan();
13         linkMan.setLkmName("小李1");
14 
15         linkMan.setCustomer(customer);
16         customer.getLinkMans().add(linkMan);
17 
18         customerDao.save(customer);
19     }

级联删除

<prop key = "hibernate.hbm2ddl.auto">update<prop>

 1     /**
 2      * 级联删除:
 3      *      删除1号客户的同时,删除1号客户的所有联系人
 4      */
 5     @Test
 6     @Transactional //配置事务
 7     @Rollback(false) //不自动回滚
 8     public void testCascadeRemove() {
 9         //1.查询1号客户
10         Customer customer = customerDao.findOne(1l);
11         //2.删除1号客户
12         customerDao.delete(customer);
13     }

 

 

多对多操作

案例:用户和角色(多对多关系)

分析步骤
1.明确表关系
多对多关系
2.确定表关系(描述 外键|中间表)
中间间表
3.编写实体类,再实体类中描述表关系(包含关系)
用户:包含角色的集合
角色:包含用户的集合
4.配置映射关系

 

User

 1   /**
 2      * 配置用户到角色的多对多关系
 3      *      配置多对多的映射关系
 4      *          1.声明表关系的配置
 5      *              @ManyToMany(targetEntity = Role.class)  //多对多
 6      *                  targetEntity:代表对方的实体类字节码
 7      *          2.配置中间表(包含两个外键)
 8      *                @JoinTable
 9      *                  name : 中间表的名称
10      *                  joinColumns:配置当前对象在中间表的外键
11      *                      @JoinColumn的数组
12      *                          name:外键名
13      *                          referencedColumnName:参照的主表的主键名
14      *                  inverseJoinColumns:配置对方对象在中间表的外键
15      */
16     @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
17     @JoinTable(name = "sys_user_role",
18             //joinColumns,当前对象在中间表中的外键
19             joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
20             //inverseJoinColumns,对方对象在中间表的外键
21             inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
22     )
23     private Set<Role> roles = new HashSet<>();

 

Role

1     //配置多对多
2     @ManyToMany(mappedBy = "roles")  //配置多表关系
3     private Set<User> users = new HashSet<>();

 

Test

 1     /**
 2      * 保存一个用户,保存一个角色
 3      *
 4      *  多对多放弃维护权:被动的一方放弃
 5      */
 6     @Test
 7     @Transactional
 8     @Rollback(false)
 9     public void  testAdd() {
10         User user = new User();
11         user.setUserName("小李");
12 
13         Role role = new Role();
14         role.setRoleName("java程序员");
15 
16         //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
17         user.getRoles().add(role);
18 
19         //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
20         role.getUsers().add(user);
21 
22         userDao.save(user);
23         roleDao.save(role);
24     }

 

级联添加

确定主体用户,在user实体类上添加cascade属性

    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    //测试级联添加(保存一个用户的同时保存用户的关联角色)
    @Test
    @Transactional
    @Rollback(false)
    public void  testCasCadeAdd() {
        User user = new User();
        user.setUserName("zhangsan");

        Role role = new Role();
        role.setRoleName("lisi");

        //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
        user.getRoles().add(role);

        //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
        role.getUsers().add(user);
        userDao.save(user);
    }

级联删除

 1     /**
 2      * 案例:删除id为1的用户,同时删除他的关联对象
 3      */
 4     @Test
 5     @Transactional
 6     @Rollback(false)
 7     public void  testCasCadeRemove() {
 8         //查询1号用户
 9         User user = userDao.findOne(1l);
10         //删除1号用户
11         userDao.delete(user);
12 
13     }

 

 

对象导航查询

 

1.对象导航查询
  查询一个对象的同时,通过此对象查询他的关联对象

案例:客户和联系人
从一方查询多方
 默认:使用延迟加载(****)

从多方查询一方
默认:使用立即加载

  

Test

查询客户,查询到客户下多个联系人

 1         //测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
 2         @Test
 3         @Transactional // 解决在java代码中的no session问题
 4         public void  testQuery1() {
 5             //查询id为1的客户
 6             Customer customer = customerDao.getOne(9l);
 7             //对象导航查询,此客户下的所有联系人
 8             Set<LinkMan> linkMans = customer.getLinkMans();
 9 
10             for (LinkMan linkMan : linkMans) {
11                 System.out.println(linkMan);
12             }
13         }
 对象导航查询:
默认使用的是延迟加载的形式查询的
调用get方法并不会立即发送查询,而是在使用关联对象的时候才会差和讯
延迟加载
修改配置,将延迟加载改为立即加载
fetch,需要配置到多表映射关系的注解上

Customer

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<>();


从联系人对象导航查询他的所属客户
 1 /**
 2          *  默认 : 立即加载
 3          *  延迟加载:
 4          *
 5          */
 6         @Test
 7         @Transactional // 解决在java代码中的no session问题
 8         public void  testQuery3() {
 9             LinkMan linkMan = linkManDao.findOne(2l);
10             //对象导航查询所属的客户
11             Customer customer = linkMan.getCustomer();
12             System.out.println(customer);
13         }

 






 

 


posted @ 2019-09-01 15:16  曲阳阳  阅读(160)  评论(0编辑  收藏  举报