什么是DDD值对象?
实现方式
利用JPA 中的@Embeddable
和@Embedded
两个注解来实现值对象的持久化
单一值对象
单一对象可以利用嵌入的方式将值对象的属性映射到实体类,将值对象的属性 映射成表的字段 。
录入学生信息时,需要一个家庭住址的信息,则这个地址信息则可以设计成一个值对象AddressModel
。
import lombok.Data;
import javax.persistence.Embeddable;
/**
* @author peter
* date 2020/11/12 17:03
*/
@Data
@Embeddable
public class AddressModel {
private String city;
private String street;
private Long lng;
private Long lat;
}
学生Student
import lombok.Data;
import javax.persistence.*;
/**
* @author peter
* date 2020/11/12 17:08
*/
@Entity
@Data
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Embedded
private AddressModel homeAddress;
//省略其他信息....
}
在数据库中`student`表结构
改变表中映射的字段的名称
利用@AttributeOverride
注解来修改映射字段在数据库表的字段名称
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city",column = @Column(name = "address_city")),
@AttributeOverride(name = "street",column = @Column(name = "address_street")),
@AttributeOverride(name = "lng",column = @Column(name = "address_lng")),
@AttributeOverride(name = "lat",column = @Column(name = "address_lat")),
})
private AddressModel homeAddress;
根据值对象的单一属性查询或多属性的动态查询
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.util.StringUtils;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
/**
* @author peter
* date 2020/11/12 17:11
*/
public interface StudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> {
/**
* 根据地址中的 city 来查询
*
* @param city
* @return
*/
List<Student> findByHomeAddress_City(String city);
/**
* 根据city、street 来动态查询
* @param city
* @param street
* @return
*/
default List<Student> query(String city, String street) {
Specification<Student> specification = new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(city)) {
predicates.add(cb.equal(root.get(Student_.HOME_ADDRESS).get("city"), city));
}
if (StringUtils.hasText(street)) {
predicates.add(cb.equal(root.get(Student_.HOME_ADDRESS).get("street"), street));
}
return cb.and(predicates.toArray(new Predicate[0]));
}
};
return findAll(specification);
}
}
多个值对象
利用 @ElementCollection
注解来实现持久化,在数据库会自动创建一个单独用于保存值对象数据的表,当实体类被删除时,自动删除对应的值对象的数据。
老师Teacher
import lombok.Data;
import javax.persistence.*;
import java.util.Set;
/**
* @author peter
* date 2020/11/12 18:15
*/
@Data
@Entity
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ElementCollection
private Set<AddressModel> homeAddresses;
//省略其他信息....
}
数据库中值对象表结构
根据值对象的单一属性查询或多属性的动态查询
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.util.StringUtils;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author peter
* date 2020/11/12 18:30
*/
public interface TeacherRepository extends JpaRepository<Teacher,Long>, JpaSpecificationExecutor<Teacher> {
/**
* 根据地址中的 city 来查询
*
* @param city
* @return
*/
List<Teacher> findByHomeAddresses_City(String city);
/**
* 根据city、street 来动态查询
*
* @param city
* @param street
* @return
*/
default List<Teacher> query(String city, String street) {
Specification<Teacher> specification = new Specification<Teacher>() {
@Override
public Predicate toPredicate(Root<Teacher> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
SetJoin<Teacher, AddressModel> addressModelSetJoin = root.joinSet(Teacher_.HOME_ADDRESSES);
if (StringUtils.hasText(city)) {
predicates.add(cb.equal(addressModelSetJoin.get("city"), city));
}
if (StringUtils.hasText(street)) {
predicates.add(cb.equal(addressModelSetJoin.get("street"), street));
}
return cb.and(predicates.toArray(new Predicate[0]));
}
};
return findAll(specification);
}
}