springdata_day02&03

1. SpringDataJpa的概述

1. 概述

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

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

2. 特性

使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法

3. Spring Data JPA和JPA以及hibernate的关系

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

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

2. 快速入门

1. 需求分析

完成客户的基本CRUD操作

2. 搭建开发环境

1. 引入坐标
<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. 整合springdata JPA与spring
<?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. 创建entityManagerFactory对象交给spring容器管理-->
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--配置扫描的包,实体类所在的包-->
    <property name="packagesToScan" value="cn.itcast.domain"/>
    <!--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"/>
      </bean>
    </property>

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

  <!--2. 创建数据库连接池-->
  <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>

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

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

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

  <!--6. 配置包扫描-->
  <context:component-scan base-package="cn.itcast"/>
</beans>
3. 编写实体类

使用jpa注解配置映射关系,同之前的

3. 编写dao层接口

只需要编写dao接口,不需要编写实现类

dao层接口规范:

  1. 需要继承两个接口(JpaRepository,JpaSpecificationExecutor)
  2. 需要提供相应的泛型
  • JpaRepository<操作的实体类类型,实体类中主键属性的类型> 封装了基本CRUD操作
  • JpaSpecificationExecutor<操作的实体类类型> 封装了复杂查询(分页)

4. 增删改查

findOne(id):根据id查询

save:保存或更新

根据传递的对象是否存在主键id,如果没有id主键属性:保存;存在id主键属性,更新数据

delete(id):根据id删除

findAll():查询全部

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

  @Autowired
  private CustomerDao customerDao;

  @Test
  public void testFindOne(){
    Customer customer = customerDao.findOne(2L);
    System.out.println(customer);
  }

  @Test
  public void testSave(){
    Customer customer = new Customer();
    customer.setCustName("zhangsan");
    customer.setCustLevel("vip");
    customer.setCustIndustry("金融");
    customerDao.save(customer);
  }

  @Test
  public void testUpdate(){
    Customer customer = new Customer();
    customer.setCustId(9L);
    customer.setCustName("lisi");

    customerDao.save(customer);
  }

  @Test
  public void testDelete(){
    customerDao.delete(9L);
  }

  @Test
  public void testFindAll(){
    List<Customer> list = customerDao.findAll();
    for (Customer customer : list) {
      System.out.println(customer);
    }
  }
}

5. 复杂查询

1. 借助接口中定义好的方法完成查询

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

  1. 可以查询id为4的客户,如果值为空,不存在;不为空,存在
  2. 判断数据库中id为4的客户的数量,如果为0,不存在;大于0,存在
// 测试统计查询:查询客户数量
@Test
public void testCount(){
  long count = customerDao.count();
  System.out.println(count);
}

// 根据id从数据库查询 事务注解保证正常运行
@Test
public void testExists(){
  boolean exists = customerDao.exists(4L);
  System.out.println("id 为4的客户 是否存在:"+exists);
}

@Test
@Transactional
public void testGetOne(){
  Customer customer = customerDao.getOne(4L);
  System.out.println(customer);
}
2. jpql查询

需要将jpql语句配置到接口方法上

针对特有的查询,在新添加的方法上,使用注解的形式(@Query)配置jpql查询语句

对于多个占位符参数,赋值的时候

  • 默认情况下,占位符的位置需要和方法参数的位置保持一致

  • 可以指定占位符参数的位置, ?索引的方式,指定此占位的取值来源

    eg. @Query(value = "from Customer where custName=?2 and custId=?1")

springDataJpa使用jpql完成 更新/删除操作

  • 需要手动添加事务的支持
  • 默认会执行结束后,回滚事务,需要设置是否自动回滚
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {

  // 案例1:根据客户行业查询客户
  @Query(value = "from Customer where custIndustry=?")
  public Customer findJpql(String custIndustry);

  // 案例2:根据客户名称和客户id查询客户
  @Query(value = "from Customer where custName=? and custId=?")
  public Customer findByCustNameAndId(String name,Long id);

  // 根据id更新,客户的名称
  // @Query 代表的是进行查询,需要声明该方法是进行更新操作
  @Query(value = "update Customer set custName=?2 where custId=?1")
  @Modifying
  public void updateCustom(long custId,String custName);
}
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class JpqlTest {
  @Autowired
  private CustomerDao customerDao;

  @Test
  public void testFindJpql(){
    Customer customer = customerDao.findJpql("电商1");
    System.out.println(customer);
  }

  @Test
  public void testFindByCustNameAndId(){
    Customer customer = customerDao.findByCustNameAndId("tom", 2L);
    System.out.println(customer);
  }

  // 测试jpql的更新操作
  @Test
  @Transactional
  @Rollback(value = false)
  public void testUpdateCustomer(){
    customerDao.updateCustom(4L,"猪八戒");
  }
3. SQL语句的查询
  1. 特有的查询:需要在dao接口上配置方法

  2. 在新添加的方法上,使用注解(@Query)的形式配置sql查询语句

    注解:@Query 属性value jpql语句 | SQL语句

    nativeQuery:false | true 是否使用本地查询(也就是sql)

4. 方法名称规则查询

是对jpql查询,更加深入的一层封装

只需要按照springDataJpa提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询

方法名的约定:

  • findBy开头:代表查询 对象中属性的名称(首字母大写)

    含义:根据属性名称进行查询

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

public List<Customer> findByCustNameLike(String custName);

//使用客户名称模糊匹配和客户所属行业精准匹配的查询
public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
// 测试方法命名规则的查询
@Test
public void testNaming(){
  Customer customer = customerDao.findByCustName("猪八戒");
  System.out.println(customer);
}


// 测试方法命名规则的查询
@Test
public void testFindByCustNameLike(){
  List<Customer> customerList = customerDao.findByCustNameLike("猪八%");
  for (Customer customer : customerList) {
    System.out.println(customer);
  }
}

// 测试方法命名规则的查询
@Test
public void testFindByCustNameLikeAndCustIndustry(){
  Customer customer = customerDao.findByCustNameLikeAndCustIndustry("猪八%", "电商1");
  System.out.println(customer);
}

3. 内部原理

1. 实现过程

在这里插入图片描述

4. Specifications动态查询

1. JpaSpecificationExecutor方法列表

  • T findOne(Specification<T> var1); 查询单个对象

  • List<T> findAll(Specification<T> var1); 查询列表

  • Page<T> findAll(Specification<T> var1, Pageable var2); 查询全部:分页

    其中page是SpringDataJpa提供的

  • List<T> findAll(Specification<T> var1, Sort var2); 查询列表,排序

  • long count(Specification<T> var1); 统计查询

2. Specification:查询条件

1. 概念

自定义自己的Specification实现类

实现:

Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);

参数:

  • root 查询的根对象(查询的任何属性都可以从根对象中获取)
  • CriteriaQuery:顶层的查询对象,自定义查询方式(了解,一般不用)
  • CriteriaBuilder:查询的构造器,封装了很多查询条件
2. 代码范例
  • 多条件查询
  • 模糊查询
  • 分页查询
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class SpecTest {
  @Autowired
  private CustomerDao customerDao;

  /**
     *  匿名内部类
     *
     *  自定义查询条件
     *      1. 实现Specification接口(提供泛型,查询的对象类型)
     *      2. 实现toPredicate方法(构造查询条件)
     *      3. 需要借助方法参数的两个参数(
     *          root:获取需要查询的对象属性
     *          CriteriaBuilder:构造查询条件,内部封装很多的查询条件(精确匹配,模糊匹配)
     *      )
     *   案例: 根据客户名称查询,查询客户名为猪八戒的客户
     *      查询条件
     *          1. 查询方式
     *              cb对象
     *          2. 比较的属性名称
     *              root对象
     */
  @Test
  public void testSpec(){
    Specification<Customer> spec= new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
        // 1. 获取比较的属性
        Path<Object> custName = root.get("custName");
        // 2. 构造查询条件
        Predicate predicate = cb.equal(custName, "猪八戒");//进行精准匹配(需要比较的属性,比较的属性的取值)
        return predicate;
      }
    };
    Customer customer = customerDao.findOne(spec);
    System.out.println(customer);
  }

  /**
     *  多条件查询
     *      案例:根据客户名(猪八戒)和客户所属行业查询(电商1)
     *      root:获取属性
     *          客户名
     *          所属行业
     *      cb:构造查询
     *          1.构造客户名的精准匹配查询
     *          2. 构造所属行业的精准匹配查询
     *          3. 将以上两个查询联系起来
     */
  @Test
  public void testMultiSpec(){
    Specification<Customer> spec = new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
        Path<Object> custName = root.get("custName");
        Path<Object> custIndustry = root.get("custIndustry");
        // 构造查询
        Predicate p1 = cb.equal(custName, "猪八戒");
        Predicate p2 = cb.equal(custIndustry, "电商1");
        // 组合
        Predicate and = cb.and(p1, p2);//与
        //Predicate or = cb.or(p1, p2);//或
        return and;
      }
    };
    Customer customer = customerDao.findOne(spec);
    System.out.println(customer);
  }

  /**
     *  案例:完成根据客户行业的模糊匹配,返回客户列表
     *      客户行业以“电商”开头
     *  equal:直接得到path对象(属性),然后比较
     *  gt,lt,ge,le,like:得到path对象,根据path指定比较的参数类型,再比较
     *      指定参数类型:path.as(类型的字节码对象)
     */
  @Test
  public void testSpec3(){
    // 构造查询条件
    Specification<Customer> spec = new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
        // 查询属性:客户行业
        Path<Object> custIndustry = root.get("custIndustry");
        // 查询方式:模糊匹配
        Predicate like = cb.like(custIndustry.as(String.class), "电商%");
        return like;
      }
    };

    /**
         *  添加排序
         *  创建排序对象,需要调用构造方法实例化sort对象
         *  第一个参数:排序的顺序(正序、倒序)
         *      Sort.Direction.DESC
         *      Sort.Direction.ASC
         *  第二个参数:排序的属性名称
         */
    Sort sort = new Sort(Sort.Direction.DESC,"custId");
    List<Customer> customerList = customerDao.findAll(spec,sort);
    for (Customer customer : customerList) {
      System.out.println(customer);
    }
  }

  /**
     *  分页查询
     *      Specification:查询条件
     *      Pageable: 分页参数
     *          分页参数:查询的页码,每页查询的条数
     *          findAll(Specification,Pageable) 带有条件的分页
     *          findAll(Pageable):每页条件的分页
     *      返回:Page(SpringDataJpa为我们封装好的pageBean对象,数据列表,总条数
     */
  @Test
  public void testSpec4(){
    Specification spec = null;
    /**
         *  PageRequest对象是Pageable接口的实现类
         *  创建PageRequest的过程,需要调用它的构造方法传入两个参数
         *      第一个参数:当前查询的页数(从0开始)
         *      第二个参数:每页查询的数量
         */
    Pageable pageable = new PageRequest(0,2);
    Page<Customer> page = customerDao.findAll(null, pageable);
    System.out.println(page.getTotalElements());// 总条数
    System.out.println(page.getContent());// 数据集合列表
    System.out.println(page.getTotalPages());//总页数
  }
}

5. 多表之间关系和操作多表

1. 概念

1. 表关系
一对一
一对多:
	一的一方:主表
	多的一方:从表
	外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
多对多:
	中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键
2. 实体类中的关系

包含关系:可以通过实体类中的包含关系描述表关系

继承关系

3. 分析步骤

1.明确表关系

2.确定表关系(描述 外键|中间表)

3.编写实体类,再实体类中描述表关系(包含关系)

4.配置映射关系

2. 案例

1. 一对多操作
案例:客户和联系人的案例(一对多关系)

客户:一家公司;联系人:这家公司的员工

一个客户可以具有多个联系人;一个联系人从属于一家公司

分析步骤
1.明确表关系
	一对多关系
2.确定表关系(描述 外键|中间表)
	主表:客户表
	从表:联系人表
		* 在从表上添加外键
3.编写实体类,再实体类中描述表关系(包含关系)
	客户:在客户的实体类中包含一个联系人的集合
	联系人:在联系人的实体类中包含一个客户的对象
4.配置映射关系
	* 使用jpa注解配置一对多映射关系

使用注解的形式配置多表关系

  1. 声明关系

    @OneToMany: 配置一对多关系

    ​ targetEntity: 对方对象的字节码对象

  2. 配置外键(中间表)

    @JoinColumn: 配置外键

    ​ name:外键字段名称

    ​ referencedColumnName:参照的主表的主键字段名称

在客户实体类上(一的一方)添加了外键的配置,所以对于客户而言,也具备了维护外键的作用

配置联系人到客户的多对一关系,使用注解的形式配置多对一的关系

  1. 配置表关系

    @ManyToOne: 配置多对一关系

    ​ targetEntity:对方的实体类字节码

  2. 配置外键(中间表)

配置外键的过程,配置到了多的一方,就会在多的一方维护外键

@Entity//声明实体类
@Table(name = "cst_customer")//配置实体类和表的映射关系
public class Customer implements Serializable {

  @Id//声明主键的配置
  @GeneratedValue(strategy = GenerationType.IDENTITY)//配置主键的生成策略,自增
  @Column(name = "cust_id")//配置属性和字段的映射关系
  private Long custId;

// ...

  // 配置客户和联系人的关系(一对多关系)
  @OneToMany(targetEntity = LinkMan.class)
  @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
  private Set<LinkMan> linkMans = new HashSet<LinkMan>();
@Entity
@Table(name = "cst_linkman")
public class LinkMan implements Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @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;// 联系人备注

  @ManyToOne(targetEntity = Customer.class)
  @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
  private Customer customer;
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class One2ManyTest {

  @Autowired
  private CustomerDao customerDao;

  @Autowired
  private LinkManDao linkManDao;

  /**
     *  保存一个客户,保存一个联系人
     */
  @Test
  @Transactional
  @Rollback(false)
  public void testAdd(){
    // 创建一个客户,创建一个联系人
    Customer customer = new Customer();
    customer.setCustName("百度");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("小李");

    // 配置客户到联系人的关系(一对多)
    customer.getLinkMans().add(linkMan);

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

其实有些冗余,可以放弃一方(客户)对外键的维护权,

@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
删除
  • 删除从表数据,随意
  • 删除主表数据
  1. 有从表数据:
  • 在默认情况下,把外键字段设为null,然后删除主表数据,如果在数据库的表结构上,外键字段有非空约束,会报错
  • 如果配置了放弃维护关联关系的权利,不能删除,删除时,不会更新从表的外键字段
  • 如果还想删除,使用级联删除引用
  1. 没有从表数据引用:随便删除

实际开发中,级联删除慎用(一对多的情况下)

2. 级联

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

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

customer类

// 配置客户和联系人的关系(一对多关系)
//    @OneToMany(targetEntity = LinkMan.class)
//    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();

修改jpa的配置

<property name="jpaProperties">
  <props>
    <prop key="hibernate.hbm2ddl.auto">update</prop>
  </props>
</property>

级联删除

// 级联删除:删除1号客户的同时,删除对应的所有联系人
@Test
@Transactional
@Rollback(false)
public void testCascadeRemove(){
  // 查询1号客户
  Customer customer = customerDao.findOne(1L);
  // 删除1号客户
  customerDao.delete(customer);
}
3. 多对多

案例为:用户和角色

1. 建立实体类关系和映射配置

用户实体类

@Entity
@Table(name = "sys_user")
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "user_id")
  private Long userId;

  @Column(name = "user_name")
  private String userName;

  @Column(name = "age")
  private Integer age;

  // 配置用户到角色的多对多关系
  @ManyToMany(targetEntity = Role.class)
  @JoinTable(name = "sys_user_role",
             // 当前对象在中间表中的外键
             joinColumns = {@JoinColumn(name="sys_user_id",referencedColumnName = "user_id")},
             // 对方对象在中间表的外键
             inverseJoinColumns = {@JoinColumn(name="sys_role_id",referencedColumnName = "role_id")}
            )
  private Set<Role> roles = new HashSet<Role>();

角色实体类

@Entity
@Table(name = "sys_role")
public class Role {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "role_id")
  private Long roleId;

  @Column(name = "role_name")
  private String roleName;

  @ManyToMany(mappedBy = "roles")
  private Set<User> users = new HashSet<User>();
  // ...

多对多关系,被动的一方放弃维护权。角色的配置为上面代码所示

保存操作

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

  @Autowired
  private UserDao userDao;

  @Autowired
  private RoleDao roleDao;

  // 保存一个用户,保存一个角色
  @Test
  @Transactional
  @Rollback(false)
  public void testAdd(){

    User user = new User();
    user.setUserName("小王");

    Role role = new Role();
    role.setRoleName("java程序员");

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

级联同一对多。

3. 多表的查询

1. 对象导航查询

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

案例:客户和联系人

  • 从一方查询多方

对象导航查询,默认使用延迟加载的形式查询,调用get方法并不会立即发送查询,而是在使用关联对象的时候才会查询

如果想改为立即加载,如何?

fetch,需要配置到多表映射关系的注解上

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
  • 从多方查询一方:

从表(联系人)对象导航查询他的所属客户(公司),默认是立即加载

示范代码

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

  @Autowired
  private CustomerDao customerDao;

  @Autowired
  private LinkManDao linkManDao;

  // 测试id为1的客户
  // could not initialize proxy - no Session
  @Test
  @Transactional// 解决no session
  public void testQuery1(){
    // 查询id为1的客户
    Customer customer = customerDao.getOne(2L);
    // 对象导航查询,此客户下的所有联系人
    Set<LinkMan> linkMans = customer.getLinkMans();
    for (LinkMan linkMan : linkMans) {
      System.out.println(linkMan);
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值