-
一对多操作
案例:客户和联系人的案例(一对多关系)
客户:一家公司
联系人:这家公司的员工一个客户可以具有多个联系人 一个联系人从属于一家公司 分析步骤 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.明确表关系 多对多关系 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());
}
}