JPA之 一对多与多对多

  1. 一对多操作
    案例:客户和联系人的案例(一对多关系)
    客户:一家公司
    联系人:这家公司的员工

     	一个客户可以具有多个联系人
     	一个联系人从属于一家公司
     	
     分析步骤
     	1.明确表关系
     		一对多关系
     	2.确定表关系(描述 外键|中间表)
     		主表:客户表
     		从表:联系人表
     			* 再从表上添加外键
     	3.编写实体类,再实体类中描述表关系(包含关系)
     		客户:再客户的实体类中包含一个联系人的集合
     		联系人:在联系人的实体类中包含一个客户的对象
     	4.配置映射关系
     		* 使用jpa注解配置一对多映射关系
    
     级联:
     	操作一个对象的同时操作他的关联对象
     	
     	级联操作:
     		1.需要区分操作主体
     		2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
     		3.cascade(配置级联)
     	级联添加,
     		案例:当我保存一个客户的同时保存联系人
     	级联删除
     		案例:当我删除一个客户的同时删除此客户的所有联系人
    

常用注解说明
@OneToMany:
作用:建立一对多的关系映射
属性:
targetEntityClass:指定多的一方类的字节码
mappedBy:指定从表实体类中引用主表对象的名称。
cascade:指定要使用的级联操作

	    	CascadeType.MERGE	级联更新
		    CascadeType.PERSIST	级联保存:
			CascadeType.REFRESH 级联刷新:
			CascadeType.REMOVE	级联删除:
			CascadeType.ALL		包含所有

fetch:指定是否采用延迟加载

			FetchType.LAZY  懒加载
			FetchType.EAGER 立即加载

orphanRemoval:是否使用孤儿删除

@ManyToOne
作用:建立多对一的关系
属性:
targetEntityClass:指定一的一方实体类字节码
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn
作用:用于定义主键字段和外键字段的对应关系。
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息
客户

package com.emon.demo.entity;

import lombok.Data;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
 * 1.实体类和表的映射关系
 * @Entity
 * @Table
 * 2.类中属性和标中字段的映射关系
 * @Id
 * @GeneratedValue
 * @Column
 */

@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_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;

    /**
     * 删除主表数据:
     *
     * 	有从表数据
     *   1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表                结构上,外键字段有非空约束,默认情况就会报错了。
     *   2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null,		没有关系)因为在删除时,它根本不会去更新从表的外键字段了。
     *   3、如果还想删除,使用级联删除引用
     *
     * 	没有从表数据引用:随便删
     *
     *级联操作
     * cascade:配置级联操作
     * 		CascadeType.MERGE	级联更新
     * 		CascadeType.PERSIST	级联保存:
     * 		CascadeType.REFRESH 级联刷新:
     * 		CascadeType.REMOVE	级联删除:
     * 		CascadeType.ALL		包含所有
     */
    // 配置客户和联系人之间的关系 一对多关系
    // 放弃外键维护,参照对方来做,mappedBy填写 linkMan 的配置的对应属性名
    // fetch 配置关联对象的加载方式
    // EAGER:立即加载
    // Lazy :延迟加载
    @OneToMany(mappedBy="customer",cascade = CascadeType.ALL)
    private Set<LinkMan> linkMans = new HashSet<>();

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public Set<LinkMan> getLinkMans() {
        return linkMans;
    }

    public void setLinkMans(Set<LinkMan> linkMans) {
        this.linkMans = linkMans;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custAddress='" + custAddress + '\'' +
                ", custPhone='" + custPhone + '\'';
    }
}

联系人

package com.emon.demo.entity;

import javax.persistence.*;

@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;

    @ManyToOne(targetEntity = Customer.class,fetch = FetchType.EAGER)
    @JoinColumn(name="lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;

    public Long getLkmId() {
        return lkmId;
    }

    public void setLkmId(Long lkmId) {
        this.lkmId = lkmId;
    }

    public String getLkmName() {
        return lkmName;
    }

    public void setLkmName(String lkmName) {
        this.lkmName = lkmName;
    }

    public String getLkmGender() {
        return lkmGender;
    }

    public void setLkmGender(String lkmGender) {
        this.lkmGender = lkmGender;
    }

    public String getLkmPhone() {
        return lkmPhone;
    }

    public void setLkmPhone(String lkmPhone) {
        this.lkmPhone = lkmPhone;
    }

    public String getLkmMobile() {
        return lkmMobile;
    }

    public void setLkmMobile(String lkmMobile) {
        this.lkmMobile = lkmMobile;
    }

    public String getLkmEmail() {
        return lkmEmail;
    }

    public void setLkmEmail(String lkmEmail) {
        this.lkmEmail = lkmEmail;
    }

    public String getLkmPosition() {
        return lkmPosition;
    }

    public void setLkmPosition(String lkmPosition) {
        this.lkmPosition = lkmPosition;
    }

    public String getLkmMemo() {
        return lkmMemo;
    }

    public void setLkmMemo(String lkmMemo) {
        this.lkmMemo = lkmMemo;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    @Override
    public String toString() {
        return "LinkMan{" +
                "lkmId=" + lkmId +
                ", lkmName='" + lkmName + '\'' +
                ", lkmGender='" + lkmGender + '\'' +
                ", lkmPhone='" + lkmPhone + '\'' +
                ", lkmMobile='" + lkmMobile + '\'' +
                ", lkmEmail='" + lkmEmail + '\'' +
                ", lkmPosition='" + lkmPosition + '\'' +
                ", lkmMemo='" + lkmMemo + '\'';
    }
}

Dao

package com.emon.demo.dao;

import com.emon.demo.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

/**
 * 符合SpringDataJpa的dao层接口规范
 *  JpaRepository<操作的实体类类型,实体类中主键属性的类型>
 *      *封装了基本的crud操作
 *  JpaSpecificationExecutor<操作实体类类型>、
 *      *封装了复杂的查询(分页等)
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {

    /**
     * 使用客户名称查询客户
     * 使用jpql的形式查询
     * jpql: from Customer where cust_name = ?
     * 配置jpql语句,使用@Query注解
     */

    @Query(value="from Customer where cust_name = :cust_name")
    List<Customer> findJpql(@Param("cust_name") String cust_name);

    /**
     * 根据:客户名称和客户id查询客户
     */
    @Query(value="from Customer where cust_name = :cust_name and cust_id = :id")
    List<Customer> findNameAndId(@Param("cust_name") String cust_name,@Param("id") Long cust_id);

    /**
     * 根据:更新客户名
     * @Query;代表进行查询
     * @Modifying 代表的是当前执行的是一个更新操作
     */

    @Query(value=" update Customer set cust_name= :cust_name where cust_id = :cust_id")
    @Modifying
    void updateCustomer(@Param("cust_name") String cust_name,@Param("cust_id") Long cust_id);

    /**
    * 使用sql的形式查询
     * sql:select * from cst_customer
     * 使用原生sql查询的结构都是 List<Object[]>    </>
     */
    @Query(value=" select * from cst_customer",nativeQuery = true)
    List<Object[]> findBysql();

    @Query(value=" select * from cst_customer where cust_name like ?1%",nativeQuery = true)
    List<Object[]> findBysqlName(String name);

    /**
     * 方法名的约定
     * findBy :查询、
     * 对象中的属性名(首字母大写);查询条件
     * CustName
     * findByCustName  -- 根据客户名称查询
     * 在springdataJpa的运行阶段
     * 会根据方法名进行解析 findBy from xxx(实体类)
     *                          属性名称  where custName =
     *  1.findBy + 属性名称(根据属性名查询)
     *  2.findBy + 属性名称 + 查询方式(like | isnull) 如: findByCustNameLike
     *  3.多条件查询
     *    findBy + 属性名 + “查询方式” + "多条件的连接符(and|or)" + 属性名 + “查询方式”
     */
    Customer findByCustName(String custName);

    /**
     * 模糊查询
     * @param custName
     * @return
     */
    List<Customer> findByCustNameLike(String custName);

    /**
     * 使用客户名称模糊匹配和客户所属行业精准匹配的查询
     */
    Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
}

package com.emon.demo.dao;

import com.emon.demo.entity.LinkMan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

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

}

接下来就是具体使用了,这里我用的单元测试做demo,具体如下:

package com.emon.demo;

import com.emon.demo.dao.CustomerDao;
import com.emon.demo.dao.LinkManDao;
import com.emon.demo.entity.Customer;
import com.emon.demo.entity.LinkMan;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Example;
import org.springframework.test.annotation.Rollback;

import javax.transaction.Transactional;


@SpringBootTest
public class ManyOneTest {
    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LinkManDao linkManDao;

    @Test
    @Transactional
    @Rollback(value = false)
    public void testAdd(){
        Customer customer = new Customer();
        customer.setCustName("小舞");
        customer.setCustLevel("benke");
        customer.setCustIndustry("史莱克学院");
        customer.setCustAddress("天斗城");
        customer.setCustPhone("114");
        customer.setCustSource("星斗大森林");
        /// customerDao.save(customer);
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小刚");
//
//        List<Customer> list = new ArrayList<>();
//        list.add(customer);
        linkMan.setCustomer(customer);
        //customer.getLinkMans().add(linkMan);
        customerDao.save(customer);
        linkManDao.save(linkMan);

    }
    @Test
    @Transactional
    @Rollback(value = false)
    public void testDel(){
        Customer customer = new Customer();
        customer.setCustId(1L);
        customer = customerDao.findOne(Example.of(customer)).get();
        customerDao.delete(customer);

    }
    @Test
    void findByCustNameLikeAndCustIndustry() {
        Customer c = customerDao.findByCustName("小舞");
        System.out.println("c = " + c);
    }
}

  1. 多对多操作
    案例:用户和角色(多对多关系)
    用户:
    角色:

     分析步骤
     	1.明确表关系
     		多对多关系
     	2.确定表关系(描述 外键|中间表)
     		中间间表
     	3.编写实体类,再实体类中描述表关系(包含关系)
     		用户:包含角色的集合
     		角色:包含用户的集合
     	4.配置映射关系
    

角色实体

package com.emon.demo.entity;

import javax.persistence.*;

@Entity
@Table(name="sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="role_id")
    private Long Id;

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


    public Long getId() {
        return Id;
    }

    public void setId(Long id) {
        Id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

用户实体

package com.emon.demo.entity;

import sun.awt.OSInfo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@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="user_age")
    private String age;

    /**
     * 配置用户到角色的多对多关系
     * 配置多对多的映射关系
     *  1.声明表关系
     *      targetEntity 对方的实体类字节码
     *  2.配置中间表(包含两个外键)
     *
     *  cascade = CascadeType.ALL 配置级联删除
     * @return
     */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name="sys_user_role",
            // 当前对象在中间表中的外键
            joinColumns = {@JoinColumn(name="sys_user_id",referencedColumnName = "user_id")},
            // inverseJoinColumns,对方对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name="sys_role_id",referencedColumnName = "role_id")}
    )
    private Set<Role> roles = new HashSet<>();

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

Dao

package com.emon.demo.dao;

import com.emon.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {

}

package com.emon.demo.dao;

import com.emon.demo.entity.Role;
import com.emon.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {

}

多表的查询
1.对象导航查询
查询一个对象的同时,通过此对象查询他的关联对象

案例:客户和联系人

从一方查询多方
	* 默认:使用延迟加载(****)
	
从多方查询一方
	* 默认:使用立即加载

具体使用如下

package com.emon.demo;

import com.emon.demo.dao.RoleDao;
import com.emon.demo.dao.UserDao;
import com.emon.demo.entity.Role;
import com.emon.demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;

import javax.transaction.Transactional;
import java.util.Optional;


@SpringBootTest
public class ManyToManyTest {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;

    /**
     * 保存一个用户,保存一个角色
     * 多对多放弃维护,被动的一方放弃维护权
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testAdd() {
        User user = new User();
        user.setUsername("小奥");

        Role role = new Role();
        role.setRoleName("学员");
        // 配置用户到角色的关系。可以对中间表的数据维护
        user.getRoles().add(role);
        roleDao.save(role);
        userDao.save(user);
        //
    }

    @Test
    @Transactional
    @Rollback(value = false)
    public void testremove() {
        Optional<User> user = userDao.findOne((root, query, cb)->
                cb.equal(root.get("userId"),1L));
        userDao.delete(user.get());
    }
}

点击链接查看源码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值