Spring Data JPA的一对多关联映射

1. Spring Data JPA一对多的关联映射案例

1.1 创建表结构

客户表的建表语句:

CREATE TABLE `cst_customer` (
	`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
	`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
	`cust_user_id` bigint(32) DEFAULT NULL COMMENT '负责人id',
	`cust_create_id` bigint(32) DEFAULT NULL COMMENT '创建人id',
	`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
	`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
	`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
	`cust_linkman` varchar(64) DEFAULT NULL COMMENT '联系人',
	`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
	`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
	PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

联系人表的建表语句:

CREATE TABLE `cst_linkman` (
	`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
	`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
	`lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
	`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
	`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
	`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
	`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
	`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
	`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
	`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
	PRIMARY KEY (`lkm_id`),
	KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
	CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

1.2 编写实体类

客户的实体类如下:

@Entity
@Table(name="cst_customer")
public class Customer implements Serializable {
	
	@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_industry")
	private String custIndustry;
	@Column(name="cust_level")
	private String custLevel;
	@Column(name="cust_address")
	private String custAddress;
	@Column(name="cust_phone")
	private String custPhone;
	
    /**
     * 配置客户和联系人的一对多关系
     * @OneToMany:建立一对多的关系映射
     * 		targetEntityClass:指定多的多方的类的字节码
     * 		mappedBy:指定从表实体类中引用主表对象的名称
     * 		cascade:指定要使用的级联操作
     * 		fetch:指定是否采用延迟加载
     * 		orphanRemoval:是否使用孤儿删除
     * @JoinColumn:用于定义主键字段和外键字段的对应关系
     * 		name:指定外键字段的名称
     * 		referencedColumnName:指定引用主表的主键字段名称
     * 		unique:是否唯一。默认值不唯一
     * 		nullable:是否允许为空。默认值允许
     * 		insertable:是否允许插入。默认值允许
     * 		updatable:是否允许更新。默认值允许
     * 		columnDefinition:列的定义信息
     */
  	@OneToMany(targetEntity=LinkMan.class)
	@JoinColumn(name="lkm_cust_id", referencedColumnName="cust_id")
	private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);

	//省略 get和 set方法
}

联系人的实体类如下:

@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:建立多对一的关系
     * 		targetEntityClass:指定多的多方的类的字节码
     * 		cascade:指定要使用的级联操作
     * 		fetch:指定是否采用延迟加载
     * 		optional:关联是否可选。如果设置为 false,则必须始终存在非空关系
     */
	@ManyToOne(targetEntity=Customer.class)
	@JoinColumn(name="lkm_cust_id", referencedColumnName="cust_id")
	private Customer customer;
	
	//省略 get和 set方法
}

2.3 编写Dao层接口

客户的Dao层接口如下:

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

联系人的Dao层接口如下:

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

2.4 编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class OneToManyTest {

	@Autowired
	private CustomerDao customerDao;
	
	@Autowired
	private LinkManDao linkManDao;

	@Test
	public void test1(){
		Customer customer = new Customer();
		customer.setCust_name("张总");
		LinkMan linkMan = new LinkMan();
		linkMan.setLkm_name("秦助理");
		
		customer.getLinkMans().add(linkMan);
		linkMan.setCustomer(customer);
		
		customerDao.save(customer);
		linkManDao.save(linkMan);
	}
}

2. Spring Data JPA一对多的相关操作

2.1 保存

@Test
public void test1(){
	Customer customer = new Customer();
	customer.setCust_name("张总");
	LinkMan linkMan = new LinkMan();
	linkMan.setLkm_name("秦助理");
	
	customer.getLinkMans().add(linkMan);
	linkMan.setCustomer(customer);
	
	customerDao.save(customer);
	linkManDao.save(linkMan);
}

通过上面的代码,我们可以发现当我们建立了双向的关联关系之后,先保存主表,再保存从表时,会产生2条insert和1条update,而实际开发中我们只需要2条insert。

原因是在客户实体类上(一的一方)添加了外键了配置,所以对于客户而言,也具备了维护外键的作用。那我们的解决是思路很简单,就是一的一方放弃维护权。放弃外键维护权需要将客户实体类的一对多配置改为:

@Entity
@Table(name="cst_customer")
public class Customer implements Serializable {
	
	//mappedBy:放弃外键维护权,值为对方配置关系的属性名称
	@OneToMany(mappedBy="customer")
	private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
}

2.2 删除

@Test
public void test2(){
	customerDao.delete(1l);
}

删除操作的说明如下:

  • 删除从表数据:可以随时任意删除。
  • 删除主表数据:
    • 有从表数据:
      1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表结构上,外键字段有非空约束,默认情况就会报错了。
      2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null没有关系)。因为在删除时,它需要把外键字段置为null,而放弃外键维护权,它根本不会去更新从表的外键字段了,所以删除时会报错。
      3、如果还想删除,使用级联删除引用。
    • 没有从表数据引用:随便删。

2.3 级联操作和延迟加载

级联操作:指操作一个对象同时操作它的关联对象。使用方法:只需要在操作主体的注解上配置cascade。

延迟加载:就是当在真正需要数据的时候,才真正执行数据加载操作。使用方法:只需要在操作主体的注解上配置fetch。

/**
 * cascade:配置级联操作
 * 		CascadeType.MERGE:级联更新
 * 		CascadeType.PERSIST:级联保存
 * 		CascadeType.REFRESH:级联刷新
 * 		CascadeType.REMOVE:级联删除
 * 		CascadeType.ALL:包含所有
 * fetch:配置关联对象的加载方式
 *      FetchType.EAGER:立即加载
 *      FetchType.LAZY:延迟加载 
 */
@OneToMany(mappedBy="customer", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值