Spring 全家桶之 Spring Data JPA(四)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Spring 全家桶之 Spring Data JPA(四)

一、多表查询之一对多

一对多

新建maven项目one2many

导入maven依赖

<properties>
    <spring.version>5.0.2.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>8.0.19</mysql.version>
</properties>
<dependencies>
    <!-- junit单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</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>
    <!-- spring对orm框架的支持包-->
    <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>
    <!-- spring data jpa 的坐标-->
    <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>

新建entity包,新增Customer实体类

@Entity //表示是一个实体类
@Table(name = "cst_customer") //映射的表明
public class Customer {
    @Id//声明主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//声明主键生成策略
    @Column(name = "cust_id") //属性和字段映射
    private Long custId;
    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_source")
    private String custSource;
    @Column(name = "cust_level")
    private String custLevel;
    @Column(name = "cust_industry")
    private String custIndustry;
    @Column(name = "cust_phone")
    private String custPhone;
    @Column(name = "cust_address")
    private String custAddress;
    /**
     * 客户和联系人之间的关系,一对多关系
     * 使用注解形式配置多表关系
     * 1.声明关系:@OneToMany配置一对多关系,targetEntity对方对象的字节码对象
     * 2.配置外键(中间表):@JoinColumn,name外键字段名称,referenceColumnName参照主表的主键字段名称
     * 在客户实体类上(一对多中一的这边)添加了外键配置,对于客户而言,具备了维护外键的作用
     */
    // 联系人
    // 客户与联系人一对多关系,用@OneToMany表示
    @OneToMany(targetEntity = LinkMan.class)
    // name是指外键名,referencedColumnName是指主表的主键
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") // 外键配置
    private Set<LinkMan> linkManSet = new HashSet<>();
    // 此处省略getter/setter/toString方法
}

LinkMan实体类

@Entity
@Table(name = "cst_linkman")
public class LinkMan {
    @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;
    /**
     * 创建联系人到客户的多对一的关系
     * 注解配置多对一关系
     * 1.配置表关系,@ManyToOne,targetEntity对方实体类的字节码
     * 2.配置外键(多对多使用中间表),
     * 配置外键的过程,配置到多的一方,就会在多的一方维护外键
     */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;
    //此处省略getter/setter/toString方法
}    

新增dao包,增加CustomerDao接口

public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer> {
}

增加LinkManDao接口

public interface LinkManDao extends JpaRepository<LinkMan, Long>, JpaSpecificationExecutor<LinkMan> {
}

resource目录下新建配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      http://www.springframework.org/schema/data/jpa
      http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <!--spring 和 spring data jpa的配置-->
    <!-- 配置包扫描-->
    <context:component-scan base-package="com.citi" ></context:component-scan>
    <!-- 1.创建entityManagerFactory对象交给spring容器管理-->
    <bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--配置的扫描的包(实体类所在的包) -->
        <property name="packagesToScan" value="com.citi.entity" />
        <!-- jpa的实现厂家 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>
        <!--jpa的供应商适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--配置是否自动创建数据库表 -->
                <property name="generateDdl" value="false" />
                <!--指定数据库类型 -->
                <property name="database" value="MYSQL" />
                <!--数据库方言:支持的特有语法 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                <!--是否显示sql -->
                <property name="showSql" value="true" />
            </bean>
        </property>
        <!--jpa的方言 :高级的特性 -->
        <property name="jpaDialect" >
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
        <!--
        注入jpa的配置信息
        记载jpa的基本配置信息和jpa实现方式的配置信息-->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>
    <!--2.创建数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai" ></property>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
    </bean>
    <!--3.整合spring dataJpa-->
    <jpa:repositories base-package="com.citi.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactoty"></jpa:repositories>
    <!--4.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoty"></property>
    </bean>
    <!-- 4.txAdvice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- 5.aop-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.citi.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
    </aop:config>
</beans>

在test包中新建测试类One2ManyTest,执行测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class One2ManyTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
    // 不配置客户到联系人或者联系人到客户的关系
    @Test
    @Transactional
    @Rollback(false) //不自动回滚
    public void testSave(){
        //创建客户
        Customer customer = new Customer();
        customer.setCustName("Peter Parker");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("Steve");
        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
}

执行的SQL语句

image.png

查看数据库,可以看到cst_linkman表的外键为空,两者没有建立关系

image.png

在One2ManyTest中增加测试方法testSave0()

// 只配置客户到联系人的关系
@Test
@Transactional
@Rollback(false) //不自动回滚
public void testSave0(){
    //创建客户
    Customer customer = new Customer();
    customer.setCustName("Peter Parker");
    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Steve");
    // 配置customer的linkman
    customer.getLinkManSet().add(linkMan);
    customerDao.save(customer);
    linkManDao.save(linkMan);
}

只配置了客户到联系人的关系,查看执行的SQL,相比上一次测试多了一条update外键的sql语句

image.png

查看数据库表,外键已更新,关联关系已经建立

image.png

在One2ManyTest中在增加testSave1()

// 只配置联系人到客户的关系
@Test
@Transactional
@Rollback(false) //不自动回滚
public void testSave2(){
    //创建客户
    Customer customer = new Customer();
    customer.setCustName("Peter Parker");
    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Steve");
    // 配置linkMan的customer
    linkMan.setCustomer(customer);
    customerDao.save(customer);
    linkManDao.save(linkMan);
}

只配置了联系人到客户的关系,查看执行的SQL语句,没有执行update语句,外键在insert的时候就已经建立

image.png

查看数据库表,外键存在,关联关系建立成功

image.png

在One2ManyTest中增加testSave2(),在linkMan中set customer,在customer中set linkman

@Test
@Transactional
@Rollback(false) //不自动回滚
public void testSave3(){
    //创建客户
    Customer customer = new Customer();
    customer.setCustName("Stark");
    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Thor");
    // 配置客户到联系人的关系
    customer.getLinkManSet().add(linkMan);
    // 配置linkMan的customer
    linkMan.setCustomer(customer);
    customerDao.save(customer);
    linkManDao.save(linkMan);
}

查看执行的SQL语句

image.png

查看数据库表关系是否建立成功

image.png

通过以上四个方法的执行,testSave()无法建立customer与linkman的关联关系,testSave0()通过在customer中set linkMan可以建立两者关系,后台执行了4条sql语句(除去建表语句),testSave1()通过在linkMan中set customer也可以建立两者之间外键关系,后台执行了3条SQL语句,testSave2()通过在customer中set linkMan,同时在linkMan中set customer也可以建立两者之间的关系,后台执行了4条SQL语句,因此可以看出在一对多关系中一的一边建立外键维护关系可以执行较少的SQL语句而完成外键关系的建立,而多的一方无需拥有外键关系的维护

Customer实体类修改,mappedBy是对方配置关系的属性名称

@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkManSet = new HashSet<>();

级联的概念是操作一个对象的同时操作他的关联对象 ,级联操作需要注意:

  • 需要区分操作主体
  • 需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
  • cascade(配置级联)

级联添加:当保存一个客户的同时保存客户的联系人

级联删除:删除一个客户的同时删除客户的联系人

级联操作测试

首先修改配置文件applicationContext.xml,将create修改为update,每次执行测试的时候不会再新建表,而是变为更新表操作

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

Customer实体类增加cascade属性

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

cascade属性可以配置

  • CascadeType.All:所有操作更新保存删除都配置级联操作
  • CascadeType.MERGE:更新配置级联操作
  • CascadeType.PERSIST:保存配置级联操作
  • CascadeType.REMOVE:删除配置级联操作
  • 在One2ManyTest中新增级联添加的测试方法
@Test
    @Transactional
    @Rollback(false) //不自动回滚
    public void testCascadeSave(){
        //创建客户
        Customer customer = new Customer();
        customer.setCustName("Peter");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("Stark");
        // 配置客户到联系人的关系
        customer.getLinkManSet().add(linkMan);
        // 配置linkMan的customer
        linkMan.setCustomer(customer);
        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
}

image.png

数据库表

image.png

在One2ManyTest中新增级联删除测试

@Test
@Transactional
@Rollback(false) //不自动回滚
public void testCascadeDelete(){
    //查询客户
    Customer customer = customerDao.findOne(1L);
    customerDao.delete(customer);
}

image.png

查看数据库,两个表中都执行了删除操作

image.png


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 Java API
如何使用 Java 记录简化 Spring Data 中的数据实体
如何使用 Java 记录简化 Spring Data 中的数据实体
38 9
|
1月前
|
SQL Java 关系型数据库
Springboot引入jpa来管理数据库
Springboot引入jpa来管理数据库
39 0
Springboot引入jpa来管理数据库
|
1月前
|
SQL Java 数据库连接
springBoot+Jpa(hibernate)数据库基本操作
springBoot+Jpa(hibernate)数据库基本操作
42 0
|
2月前
|
Java 数据库连接 API
【Java笔记+踩坑】Spring Data JPA
从常用注解、实体类和各层编写方法入手,详细介绍JPA框架在增删改查等方面的基本用法,以及填充用户名日期、分页查询等高级用法。
【Java笔记+踩坑】Spring Data JPA
|
3月前
|
安全 Java 数据安全/隐私保护
基于SpringBoot+Spring Security+Jpa的校园图书管理系统
本文介绍了一个基于SpringBoot、Spring Security和JPA开发的校园图书管理系统,包括系统的核心控制器`LoginController`的代码实现,该控制器处理用户登录、注销、密码更新、角色管理等功能,并提供了系统初始化测试数据的方法。
60 0
基于SpringBoot+Spring Security+Jpa的校园图书管理系统
|
3月前
|
Java 关系型数据库 MySQL
|
3月前
|
Java Spring 数据库
怎样动动手指就能实现数据操作?Spring Data JPA背后的魔法揭秘
【8月更文挑战第31天】在Java开发中,数据库交互至关重要。传统的JDBC操作繁琐且难维护,而Spring Data JPA作为集成JPA的数据访问层解决方案,提供了CRUD等通用操作接口,显著减少代码量。通过继承`JpaRepository`,开发者能轻松实现数据的增删改查,甚至复杂查询和分页也不再困难。本文将通过示例详细介绍如何利用Spring Data JPA简化数据访问层的开发,提升代码质量和可维护性。
43 0
|
3月前
|
存储 Java 数据库
|
3月前
|
存储 Java API
|
3月前
|
Java 数据库连接 数据库
Spring Data JPA 与 Hibernate 之区别
【8月更文挑战第21天】
92 0
下一篇
无影云桌面