前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 全家桶之 Spring Data JPA(五)

Spring 全家桶之 Spring Data JPA(五)

作者头像
RiemannHypothesis
发布2022-08-19 15:32:01
2.1K0
发布2022-08-19 15:32:01
举报
文章被收录于专栏:Elixir

一、多表操作之多对多

创建many2many项目

创建maven项目,并添加依赖

代码语言:javascript
复制
<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>

在resource目录下新增applicationContext.xml,新增以下内容

代码语言:javascript
复制
<?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="Abc*123456"></property>
        <property name="jdbcUrl" value="jdbc:mysql://rm-uf67r962043910k193o.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&amp;amp;characterEncoding=utf8&amp;amp;autoReconnect=true&amp;amp;useSSL=false&amp;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>
</beans>

新增entity包,编写两个实体类User,Role,两者为多对多关系,一个用户可以有多个角色,一个角色也包含了多个用户

代码语言:javascript
复制
public class User {

    private Long userId;

    private String userName;

    private Integer age;
    // 此处省略getter/setter/toString方法
}     
代码语言:javascript
复制
public class Role {

    private Long roleId;
    private String roleName;
    // 此处省略getter/setter/toString方法

新增dao包,编写UserDao和RoleDao,并继承JpaRepository和JpaSpecificationExecutor

代码语言:javascript
复制
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
}
代码语言:javascript
复制
public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {
}

在test包中新建测试类Many2ManyTest

代码语言:javascript
复制
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Many2ManyTest {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;
}    

配置多对多映射关系

  • 添加@Entity注解,表示该类是一个实体类
  • 增加@Table注解,表明该实体类对应的表名称
  • 增加@Id及@Column,建立实体类属性和数据库字段之间的映射关系
  • 新增角色属性,并添加getter/setter方法,用户的角色是一组集合,用Set表示
  • 在角色集合上增加@ManyToMany注解,表明多对多的关系
  • @JoinTable表示配置中间表,name表示中间表的名称,joinColumns配置的是当前对象在中间表中的外键,name值得值中间表的主键,referencedColumnName当前类对应表的主键,inverseJoinColumns:对方对象在中间表的外键 ```java @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为对方实体类的字节码
    • @JoinTable:配置中间表,name为中间表的名称,
    • joinColumns配置的是当前对象在中间表中的外键,name值得值中间表的主键,referencedColumnName当前类对应表的主键
    • inverseJoinColumns:对方对象在中间表的外键 */ @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 roleSet = new HashSet<>();

    public Set getRoleSet() { return roleSet; } public void setRoleSet(Set roleSet) { this.roleSet = roleSet; }

代码语言:javascript
复制
```java
@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(targetEntity = User.class)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")},
            inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")}
    )
    private Set<User> userSet = new HashSet<>();

    public Set<User> getUserSet() {
        return userSet;
    }

    public void setUserSet(Set<User> userSet) {
        this.userSet = userSet;
    }

多对多表操作

在Many2ManyTest中新增方法testSave()

代码语言:javascript
复制
@Test
@Transactional
@Rollback(false)
public void testSave(){

    User user = new User();
    user.setUserName("Thor");

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

    userDao.save(user);

    roleDao.save(role);

}

查看执行的SQL语句,执行了3条create语句,2条insert语句

image.png
image.png

查看数据库表,中间表没有插入数据,user和role关联关系没有建立成功

image.png
image.png
image.png
image.png
image.png
image.png

新增testSave0()方法,在user一侧建立用户到角色的关联关系

代码语言:javascript
复制
@Test
@Transactional
@Rollback(false)
public void testSave0(){

    User user = new User();
    user.setUserName("Thor");

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

    user.getRoleSet().add(role); //配置用户到角色的映射

    userDao.save(user);

    roleDao.save(role);

}

后台执行SQL如下,3条create语句,3条insert语句,中间表也被插入数据

image.png
image.png

查看数据库表,3张表中都有数据,user和role关联关系建立

image.png
image.png
image.png
image.png
image.png
image.png

同时在user和role两侧建立关联关系

代码语言:javascript
复制
@Test
@Transactional
@Rollback(false)
public void testSave1(){

    User user = new User();
    user.setUserName("Thor");

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

    // 两放都配置会产生主键冲突,只需要一方配置即可
    // 多对多放弃维护权,被动的一方放弃
    user.getRoleSet().add(role); //配置用户到角色的映射
    role.getUserSet().add(user); //配置角色到用户的映射

    userDao.save(user);

    roleDao.save(role);

}

后台执行SQL如下,摒弃饿SQL执行出现报错,因为role在执行往中间表执行insert操作时表中已经存在了user插入的数据,所以出现了主键冲突的报错

image.png
image.png

因此需要user和role一方放弃维护权,修改Role实体类中关联关系,mappedBy是指role在对方表的属性名称

代码语言:javascript
复制
//@ManyToMany(targetEntity = User.class)
//@JoinTable(name = "sys_user_role",
//        joinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")},
//        inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")}
//)
// 放弃维护权
@ManyToMany(mappedBy = "roleSet")
private Set<User> userSet = new HashSet<>();

级联添加操作,修改applicationContext.xml中的配置,从create改为update,这样每次执行时不会删除表在建立,而是直接更新

代码语言:javascript
复制
<!--
注入jpa的配置信息
记载jpa的基本配置信息和jpa实现方式的配置信息-->
<property name="jpaProperties">
    <props>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
    </props>
</property>

User类添加级联操作属性

代码语言:javascript
复制
@ManyToMany(targetEntity = Role.class, cascade = CascadeType.ALL)
@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> roleSet = new HashSet<>();

在Many2ManyTest中增加方法,执行级联添加操作

代码语言:javascript
复制
// 测试级联添加
@Test
@Transactional
@Rollback(false)
public void testCascadeSave(){

    // 操作主题为user
    User user = new User();
    user.setUserName("Peter");

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

    user.getRoleSet().add(role); //配置用户到角色的映射

    userDao.save(user);

}

后台执行SQL如下,3张表中都执行了insert操作

image.png
image.png

数据库表中成功插入数据

image.png
image.png
image.png
image.png
image.png
image.png

测试级联删除

代码语言:javascript
复制
@Test
@Transactional
@Rollback(false)
public void testCascadeDelete(){

    User one = userDao.findOne(2L);
    userDao.delete(one);

}

执行的SQL如下图

image.png
image.png

查看数据库表,三张表中关联数据已被删除

image.png
image.png
image.png
image.png
image.png
image.png

多表查询

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

使用Chapter 04 中的 one2many项目,在test包中新建ObjectQueryTest测试类

代码语言:javascript
复制
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ObjectQueryTest {

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LinkManDao linkManDao;
    
    ```
    //测试对象导航查询,查询一个对象的时候,通过此对象查询所有的关联对象
    @Test
    public void testQuery1(){
        Customer customer = customerDao.getOne(2L);
        Set<LinkMan> linkManSet = customer.getLinkManSet();
        for (LinkMan linkMan : linkManSet) {
            System.out.println(linkMan);
        }
    }
}

执行testQuery1()方法,控制台报错,需要在方法上添加@Transactional

image.png
image.png

再次执行该方法,控制台显示查询成功

image.png
image.png

新增测试方法testQuery2(),使用findOne()执行查询

代码语言:javascript
复制
@Test
@Transactional
public void testQuery2(){
    Customer customer = customerDao.findOne(2L);
    Set<LinkMan> linkManSet = customer.getLinkManSet();
    for (LinkMan linkMan : linkManSet) {
        System.out.println(linkMan);
    }
}

查询结果

image.png
image.png

对象导航查询默认使用延迟加载的形式查询,调用getOne方法不会立即发送查询,而是在使用关联对象的时候才会执行,如果将延迟加载改为立即加载,需要修改配置

fetch配置关联对象的加载方式

FetchType.LAZY:延迟加载

FetchType.EAGER:立即加载 修改Customer实体类,增加fetch配置

在ObjectQueryTest类中增加testQuery3(),从LinkMan查询Customer

代码语言:javascript
复制
@Test
@Transactional
public void testQuery3(){
    LinkMan linkMan = linkManDao.getOne(2L);
    Customer customer = linkMan.getCustomer();
    System.out.println(customer);
}

控制台输出结果如下

image.png
image.png

从一方查询多方,查询结果为集合或者列表,默认使用延迟加载

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

Spring Data JPA 完结 🎉🎉🎉

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、多表操作之多对多
    • 创建many2many项目
      • 配置多对多映射关系
        • 多对多表操作
          • 多表查询
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档