Spring Data JPA--注解--实体关联--使用/教程

本文详细介绍了Spring Data JPA中实体关联注解的使用,包括一对一、一对多、多对一和多对多关系,以及如何通过@OneToOne、@OneToMany、@ManyToOne和@ManyToMany进行配置。涵盖了级联操作、延迟加载和实例演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文网址:Spring Data JPA--注解--实体关联--使用/教程_IT利刃出鞘的博客-CSDN博客

简介

        JPA的实体关联注解实际使用的极少,因为在读写本表时会自动关联到了其他表,不够清晰。

        如果要多表联查,建议使用Specification或者在SQL里写。见:Spring Data JPA--动态查询--Specification--使用/教程/实例_IT利刃出鞘的博客-CSDN博客

注解大全

注解

说明

@OneToOne

定义表之间“一对一”的关系。

@OneToMany

定义表之间“一对多”的关系。

targetEntityClass:指定多的多方的类的字节码(常用)
mappedBy:指定从表实体类中引用主表对象的名称。(常用)
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
orphanRemoval:是否使用孤儿删除

@ManyToOne

定义表之间“多对一”的关系。

targetEntityClass:指定一的一方实体类字节码(常用)
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
optional:关联是否可选。若为 false,则必须始终存在非空关系。

@ManyToMany

定义表之间“多对多”的关系。

@JoinColumn

两张表通过某个字段进行关联时使用。

用于:“一对多”、“多对一”和“一对多”。

属性:

name :【己方】实体的【数据库字段】
referencedColumnName :【对方】实体的【数据库字段】(如果是主键,可以省略)

以下不常用
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息。

@JoinTable

两张表通过中间的关联表建立联系时使用。

用于:多对多。

属性:

name :【中间表】的【表名】
joinColumns :【己方表】与【中间表】关联(按 @OneToMany 的方式来)
inverseJoinColumns:【对方表】与【中间表】关联(按 @OneToMany 的方式来)

@PrimaryKeyJoinColumn

主键关联。在关联的两个实体中直接使用注解@PrimaryKeyJoinColumn注释。

@OneToOne/@OneToMany/@ManyToOne/@ManyToMany的属性

属性名

说明

targetEntity

指定关联实体的类名。

默认为当前标注的实体类。

cascade

表示与此实体一对一关联的实体的级联样式类型,以及当对实体进行操作时的策略。

例:用户和用户的收货地址是一对多关系,当用户被删除时,这个用户的所有收货地址也应该一并删除。订单和订单中的商品也是一对多关系,但订单被删除时,订单所关联的商品肯定不能被删除。此时只要配置正确的级联关系,就能达到想要的效果。

CascadeType.PERSIST:级联保存。当调用了Persist() 方法,会级联保存相应的数据。

CascadeType.REFRESH:级联刷新。当多个用户同时作操作一个实体,为了用户取到的数据是实时的,在用实体中的数据之前就可以调用一下refresh()方法。
CascadeType.REMOVE:级联删除。当调用remove()方法删除Order实体时会先级联删除OrderItem的相关数据。
CascadeType.MERGE:级联更新。当调用了Merge()方法,如果Order中的数据改变了会相应的更新OrderItem中的数据。
CascadeType.ALL:包含以上所有级联属性。

fetch

该实体的加载方式,包含 LAZY 和 EAGER。

LAZY(懒加载):是在属性被引用时才生成查询语句,抽取相关联数据。

EAGER(实时加载):是执行完主查询后,不管是否被引用,都会马上执行后续的关联数据查询。

使用懒加载来调用关联数据,必须要保证主查询的Session(数据库连接会话)的生命周期没有结束,否则是无法抽取到数据的。

optional

表示关联的实体是否能够存在null值。

默认为true,表示可以存在null值。如果为false,则要同时配合使用@JoinColumn标记。

mappedBy

双向关联实体时使用,标注在不保存关系的实体中。

实例

其他网址

Spring Data JPA实现多表的关联查询_pan_junbiao的博客-CSDN博客_jpa多表关联查询
SpringDataJpa中的复杂查询和动态查询,多表查询。(保姆级教程) - 掘金

一对一查询

        一对一外键关联的配置比较简单,以公民实体对象和身份证实体对象为例,在表tab_people(公民表)中添加一个字段“card_id”,作为该表的外键,同时需要保证该字段的唯一性,否则就不是一对一映射关系了,而是一对多映射关系。表tab_people和tab_idcard(身份证表)之间的关联关系如下图所示。

【示例】一对一关联,获取公民信息与身份证号码。

(1)在MySQL数据库创建tab_people表和tab_idcard表,并添加相关数据。

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tab_people;
DROP TABLE IF EXISTS tab_idcard;
 
-- 创建“身份证信息”数据表
CREATE TABLE IF NOT EXISTS tab_idcard
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '身份证ID',
    idCard_code VARCHAR(45) COMMENT '身份证号码'
) COMMENT = '身份证信息表';
 
-- 创建“公民信息”数据表
CREATE TABLE IF NOT EXISTS tab_people
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '公民ID',
    NAME VARCHAR(45) NOT NULL COMMENT '公民名称',
    sex VARCHAR(2) COMMENT '公民性别',
    age INT COMMENT '公民年龄',
    card_id INT UNIQUE COMMENT '身份证ID',
    -- 创建外键约束
    FOREIGN KEY fk_card_id (card_id)
    REFERENCES tab_idcard(id)
) COMMENT = '公民信息表';
 
-- 添加数据
INSERT INTO tab_idcard(idCard_code) VALUE('123456789');
INSERT INTO tab_people(NAME,sex,age,card_id) VALUES('pan_junbiao的博客','男',32,1); 

(2)创建名称为People.java公民信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import lombok.Data;
 
/**
 * 公民信息
 **/
@Data
@Entity
@Table(name = "tab_people")
public class People
{
    //公民ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
 
    //公民名称
    @Column(name = "name")
    private String name;
 
    //公民性别
    @Column(name = "sex")
    private String sex;
 
    //公民年龄
    @Column(name = "age")
    private int age;
 
    //关联的身份证对象
    @OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JoinColumn(name="card_id")
    private IDcard idcard;
}

(3)创建名称为IDcard.java身份证信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import lombok.Data;
 
/**
 * 身份证信息
 **/
@Data
@Entity
@Table(name = "tab_idcard")
public class IDcard
{
    //身份证ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
 
    //身份证号码
    @Column(name = "idcard_code")
    private String idCardCode;
}

(4)创建名称为PeopleDao.java公民信息数据库访问接口,并继承JpaRepository接口。

package com.pjb.jpauserdemo.dao;
 
import com.pjb.jpauserdemo.entity.People;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
/**
 * 公民信息数据库访问接口
 **/
@Repository
public interface PeopleDao extends JpaRepository<People,Integer>
{
}

(5) 测试:一对一新增(新增公民信息与关联的身份证信息)。

@Autowired
private PeopleDao peopleDao;
 
/**
 * 一对一新增:新增公民信息与关联的身份证信息
 * @author pan_junbiao
 */
@Test
public void addPeopleAndIdcard()
{
    //创建身份证信息
    IDcard idcard = new IDcard();
    idcard.setIdCardCode("123456789");
 
    //创建公民信息
    People people = new People();
    people.setName("pan_junbiao的博客");
    people.setSex("男");
    people.setAge(32);
    //将公民与身份证信息关联
    people.setIdcard(idcard);
    
    //执行新增操作
    peopleDao.save(people);
 
    //如果新增成功,则可以获取自增主键
    //否则新增失败,则抛出异常
    if(people.getId()>0)
    {
        System.out.println("新增公民信息成功!");
        System.out.println("公民ID:" + people.getId());
        System.out.println("公民名称:" + people.getName());
        System.out.println("身份证号码:" + idcard.getIdCardCode());
    }
}

执行结果:

  (6)测试:一对一查询(获取公民信息与关联的身份证信息)。

@Autowired
private PeopleDao peopleDao;
 
/**
 * 一对一查询:获取公民信息与关联的身份证信息
 */
@Test
public void getPeopleAndIdcard()
{
    People people = peopleDao.findById(1).get();
    if (people != null)
    {
        System.out.println("---------------1、公民信息--------------------");
        System.out.println("公民编号:" + people.getId());
        System.out.println("公民名称:" + people.getName());
        System.out.println("公民性别:" + people.getSex());
        System.out.println("公民年龄:" + people.getAge());
 
        //获取关联的身份证信息信息
        System.out.println("---------------2、身份证信息信息---------------");
        IDcard idCard = people.getIdcard();
        if(idCard!=null)
        {
            System.out.println("身份证ID:" + idCard.getId());
            System.out.println("身份证号码:" + idCard.getIdCardCode());
        }
    }
}

执行结果:

一对多查询

        在日常开发中一对多查询是常见的,也是业务中十分重要的部分。下面将以生产商对象(类Factory)与产品对象(类Product)为例,讲解JPA的一对多关联。类Factory与类Product的关联关系如下图。

【示例】建立生产商与产品对象对象的一对多关联,并利用映射关系查询完整的产品信息。

(1)在MySQL数据库创建tab_factory表和tab_product表,并添加相关数据。

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tab_factory;
DROP TABLE IF EXISTS tab_product;
 
-- 创建“生产商信息”数据表
CREATE TABLE IF NOT EXISTS tab_factory
( 
    factory_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '生产商ID',
    NAME VARCHAR(20) NOT NULL COMMENT '生产商名称'
) COMMENT = '生产商信息表';
 
-- 创建“产品信息”数据表
CREATE TABLE IF NOT EXISTS tab_product
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '产品ID',
    NAME VARCHAR(20) NOT NULL COMMENT '产品名称',
    price DECIMAL(9,2) NOT NULL COMMENT '产品价格',
    factory_id INT COMMENT '生产商ID'
) COMMENT = '产品信息表';
 
-- 添加数据
INSERT INTO tab_factory(NAME) VALUES('华为公司');
INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为手机',1299,1);
INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为路由器',699,1);

(2)创建名称为Product.java产品信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
 
/**
 * 产品信息
 **/
@Data
@Entity
@Table(name = "tab_product")
public class Product
{
    //产品ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
 
    //产品名称
    @Column(name = "name")
    private String name;
 
    //产品价格
    @Column(name = "price")
    private BigDecimal price;
 
    //生产商信息
    @ManyToOne
    @JoinColumn(name = "factory_id")
    private Factory factory;
}

(3)创建名称为Factory.java生产商信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import java.util.List;
import lombok.Data;
 
/**
 * 生产商信息
 **/
@Data
@Entity
@Table(name = "tab_factory")
public class Factory
{
    //生产商ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "factory_id")
    private int factoryId;
 
    //生产商名称
    @Column(name = "name")
    private String name;
 
    //产品列表
    @OneToMany
    @JoinColumn(name = "factory_id")
    private List<Product> productList;
}

(4)创建名称为FactoryDao.java生产商信息数据库访问接口,并继承JpaRepository接口。

package com.pjb.jpauserdemo.dao;
 
import com.pjb.jpauserdemo.entity.Factory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
/**
 * 生产商信息数据库访问接口
 **/
@Repository
public interface FactoryDao extends JpaRepository<Factory,Integer>
{
}

(5)创建名称为ProductDao.java产品信息数据库访问接口,并继承JpaRepository接口。

package com.pjb.jpauserdemo.dao;
 
import com.pjb.jpauserdemo.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
/**
 * 产品信息数据库访问接口
 **/
@Repository
public interface ProductDao extends JpaRepository<Product,Integer>
{
}

(6)测试:一对多查询(获取生产商信息与关联的产品列表)。

@Autowired
private FactoryDao factoryDao;
 
/**
 * 一对多查询:获取生产商信息与关联的产品列表
 */
@Test
public void getFactoryAndProductList()
{
    Factory factory = factoryDao.findById(1).get();
    if (factory != null)
    {
        System.out.println("---------------1、生产商信息信息--------------");
        System.out.println("生产商编号:" + factory.getFactoryId());
        System.out.println("生产商名称:" + factory.getName());
 
        //获取关联的产品信息信息
        System.out.println("---------------2、产品信息信息---------------");
        List<Product> productList = factory.getProductList();
        if(productList!=null && productList.size()>0)
        {
            for(Product product : productList)
            {
                System.out.println("产品编号:" + product.getId());
                System.out.println("产品名称:" + product.getName());
                System.out.println("产品价格:" + product.getPrice());
                System.out.println("-------------------");
            }
        }
    }
}

执行结果:

(7)测试:多对一查询(获取产品信息与关联的生产商信息)。

@Autowired
private ProductDao productDao;
 
/**
 * 多对一查询:获取产品信息与关联的生产商信息
 */
@Test
public void getProductAndFactory()
{
    List<Product> productList = productDao.findAll();
    if(productList!=null && productList.size()>0)
    {
        for (Product product : productList)
        {
            //获取产品信息
            System.out.println("产品编号:" + product.getId());
            System.out.println("产品名称:" + product.getName());
            System.out.println("产品价格:" + product.getPrice());
 
            //获取关联的生产商信息信息
            Factory factory = product.getFactory();
            System.out.println("生产商编号:" + factory.getFactoryId());
            System.out.println("生产商名称:" + factory.getName());
            System.out.println("-------------------");
        }
    }
}

执行结果:

多对多查询

        多对多关联关系是比较特殊的一种关联关系,它与一对一和一对多关联关系不同,需要通过另外的一张表保存多对多的映射关系。下面将以应用系统中的权限分配为例讲解多对多的关联关系,例如用户可以拥有多个系统的操作权限,而一个权限又可以被赋予多个用户,这就是典型的多对多关联映射关系。其中用户表(tab_user)和权限表(tab_user)的表关系如下图所示。

         说明:由于多对多关联关系的查询对第3个表进行反复查询,在一定程度上会影响系统的性能效率,所以在应用中尽量少使用多对多关联关系的表结果。

【示例】建立用户对象与权限对象的多对多关联关系,查询用户admin所拥有的权限,以及权限“新闻管理”被赋予了哪些用户。

(1)在MySQL数据库创建用户表(tab_user)、权限表(tab_role)和映射表(tab_mapping),并添加相关数据。

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tab_user;
DROP TABLE IF EXISTS tab_role;
DROP TABLE IF EXISTS tab_mapping;
 
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tab_user
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    NAME VARCHAR(45) NOT NULL COMMENT '用户名称'
) COMMENT = '用户信息表';
 
-- 创建“权限信息”数据表
CREATE TABLE IF NOT EXISTS tab_role
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '权限ID',
    role_name VARCHAR(45) NOT NULL COMMENT '权限名称'
) COMMENT = '权限信息表';
 
-- 创建“映射信息”数据表
CREATE TABLE IF NOT EXISTS tab_mapping
( 
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID',
    user_id INT COMMENT '用户Id',
    role_id INT COMMENT '权限Id'
) COMMENT = '映射信息表';
 
-- 添加数据
INSERT INTO tab_user(NAME) VALUES('admin'),('pan_junbiao的博客');
INSERT INTO tab_role(role_name) VALUES('系统管理员'),('新闻管理员'),('广告管理员');
INSERT INTO tab_mapping(user_id,role_id) VALUES(1,1),(1,2),(1,3),(2,2),(2,3);

(2)创建名称为User.java用户信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import java.util.List;
import lombok.Data;
 
/**
 * 用户信息的持久化类
 **/
@Data
@Entity
@Table(name = "tab_user")
public class User
{
    //用户ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
 
    //用户名称
    @Column(name = "name")
    private String name;
 
    //引用的权限实体对象集合
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "tab_mapping",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
    private List<Role> roleList;
}

(3)创建名称为Role.java权限信息的持久化类。

package com.pjb.jpauserdemo.entity;
 
import javax.persistence.*;
import lombok.Data;
 
/**
 * 权限信息
 **/
@Data
@Entity
@Table(name = "tab_role")
public class Role
{
    //权限ID
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
 
    //权限名称
    @Column(name = "role_name")
    private String roleName;
}

(4)创建名称为UserDao.java用户信息数据库访问接口,并继承JpaRepository接口。

package com.pjb.jpauserdemo.dao;
 
import com.pjb.jpauserdemo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
/**
 * 用户信息数据库访问接口
 **/
@Repository
public interface UserDao extends JpaRepository<User,Integer>
{
}

(5)测试:多对多查询(获取用户信息与关联的权限列表)。

@Autowired
private UserDao userDao;
 
/**
 * 多对多查询:获取用户信息与关联的权限列表
 */
@Test
public void getUserAndRole()
{
    List<User> userList = userDao.findAll();
    if(userList!=null && userList.size()>0)
    {
        //遍历用户列表
        for(User user : userList)
        {
            System.out.println("用户编号:" + user.getId());
            System.out.println("用户名称:" + user.getName());
 
            //获取权限列表
            List<Role> roleList = user.getRoleList();
            if(roleList!=null && roleList.size()>0)
            {
                System.out.print("用户拥有的权限:");
                for (Role role : roleList)
                {
                    System.out.print(role.getRoleName()+";");
                }
            }
            System.out.println("\n-----------------------------------------------");
        }
    }
}

执行结果:

其他网址

讲明白Spring Data JPA实体关联注解_BoCong-Deng的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT利刃出鞘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值