初识Spring Data与JPA Repository

来源:互联网 发布:vmware14运行ubuntu 编辑:程序博客网 时间:2024/06/05 02:11

1.JPA Repository

Java持久化API(JPA,JavaPersistenceAPI)是将Java对象持久化到关系型数据库的标准方式。

JPA包含两部分:用户将类匹配到关系表的映射模块以及访问对象、定义和执行查询等功能的EntityManager API。

Spring Data JPA 模块实现了Spring Data通用的Repository抽象从而进一步简化了存储的实现,这样在大多数场景无需手动实现存储类。

2.代码示例

我们领域模型中所有实体的基础类就是AbstractEntity,它使用了@MappedSuperclass来表示它本身并不是受管理的实体类,而是会由

其他的实体类进行扩展。也可以使用org.springframework.data.jpa.domain.AbstractPersistable来声明一个Long类型的id并告知持久化

提供商自动选择最合适的策略来生成主键。除此通过检查id属性实现了equals()和hashCode()方法,这样具有相同id的同种类型的实体会

被视为相等。

@MappedSuperclasspublic class AbstractEntity {   @Id   @GeneratedValue(strategy = GenerationType.AUTO)   private Long id;   public Long getId() {      return id;   }   @Override   public boolean equals(Object obj) {      if (this == obj) {         return true;      }      if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {         return false;      }      AbstractEntity that = (AbstractEntity) obj;      return this.id.equals(that.getId());   }   @Override   public int hashCode() {      return id == null ? 0 : id.hashCode();   }}
@Entity注解的普通类,如果为基础属性则不需要添加额外的注解,持久化商会自动将它们匹配到表的列上,如果需要自定义属性要到

持久化的列名,可以使用@Column注解

@Entitypublic class Address extends AbstractEntity {   private String street, city, country;   public Address(String street, String city, String country) {      Assert.hasText(street, "Street must not be null or empty!");      Assert.hasText(city, "City must not be null or empty!");      Assert.hasText(country, "Country must not be null or empty!");      this.street = street;      this.city = city;      this.country = country;   }}
@Embeddable注解,会导致持久化供应商会将其属性放到包含它的类所对应的表中

@Embeddablepublic class EmailAddress {   private static final String EMAIL_REGEX = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";   private static final Pattern PATTERN = Pattern.compile(EMAIL_REGEX);   @Column(name = "email")   private String value;   public EmailAddress(String emailAddress) {      Assert.isTrue(isValid(emailAddress), "Invalid email address!");      this.value = emailAddress;   }   protected EmailAddress() {   }   public static boolean isValid(String candidate) {      return candidate == null ? false : PATTERN.matcher(candidate).matches();   }   @Override   public String toString() {      return value;   }}
@Column注解,unique=true来保证唯一性不能被多次利用。

@OneToMany注解来指明一个Customer可以拥有多个Address,在这个注解中,我们将级联类型(cascade)设置成了CascadeType.ALL

并为Address启用了子对象移除
@JoinColumn这会导致持久化厂商为Address对象后端所对应的表添加另外一列,这一列会来引用Customer,从而实现表的关联

@Entitypublic class Customer extends AbstractEntity {   private String firstname, lastname;   @Column(unique = true)   private EmailAddress emailAddress;   @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)   @JoinColumn(name = "customer_id")   private Set<Address> addresses = new HashSet<Address>();   public Customer(String firstname, String lastname) {      Assert.hasText(firstname);      Assert.hasText(lastname);      this.firstname = firstname;      this.lastname = lastname;   }   protected Customer() {   }   public void add(Address address) {      Assert.notNull(address);      this.addresses.add(address);   }}
@Column注解其目的是将其定义为强制性的属性,添加一个Map,它会用来存储额外的属性,不同产品之间这些属性有可能不同

@Entitypublic class Product extends AbstractEntity {   @Column(nullable = false)   private String name;   private String description;   @Column(nullable = false)   private BigDecimal price;   @ElementCollection   private Map<String, String> attributes = new HashMap<String, String>();}

引入LineItem,它会持有对Product的引用以及Product的数量和购买价格,匹配Product属性时,我们使用了@ManyToOne注解,

会转化成LineItem对应表中的product_id列,这一列会用来指向Product

@Entitypublic class LineItem extends AbstractEntity {   @ManyToOne   private Product product;   @Column(nullable = false)   private BigDecimal price;   private int amount;}
LineItem的映射与前面看到的Customer和Address之间映射类似。Order会自动对LineItem实例进行级联持久化操作。因此没有必要单独管理

LineItem的持久化生命周期,其他属性都是已经讨论过的多对一的关系。

下面是Order表的定义

@ManyToOne(optional = false)这能够保证方法传递进来的Address实例如果发生变化的话,不会关联影响到已经存在的订单

@Entity@Table(name = "Orders")public class Order extends AbstractEntity {   @ManyToOne(optional = false)   private Customer customer;   @ManyToOne   private Address billingAddress;   @ManyToOne(optional = false, cascade = CascadeType.ALL)   private Address shippingAddress;   @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)   @JoinColumn(name = "order_id")   private Set<LineItem> lineItems = new HashSet<LineItem>();}
3.再来看下Spring Data

首先看下Spring Data如何为领域模型实现数据访问层,通过Spring DataRepository方式会创建一个CustomerRepository实例

下面是使用JPA来持久化前述的实体,来针对我们的存储的实现

public interface CustomerRepository extends Repository<Customer, Long> {   Customer findOne(Long id);   Customer save(Customer customer);   Customer findByEmailAddress(EmailAddress emailAddress);}
findOne()和save()方法都依靠SimpleJpaRepository来实现,实际上就是这个类的实例支撑了SpringData基础设施所创建的代理

findByEmailAddress()这是Spring Data JPA启动时就需要探查这个方法并试图根据它来衍生出查询。

ProductRepository

public interface ProductRepository extends CrudRepository<Product, Long> {   /**    * Returns a {@link Page} of {@link Product}s having a description which contains the given snippet.    *     * @param description    * @param pageable    * @return    */   Page<Product> findByDescriptionContaining(String description, Pageable pageable);   /**    * Returns all {@link Product}s having the given attribute.    *     * @param attribute    * @return    */   @Query("select p from Product p where p.attributes[?1] = ?2")   List<Product> findByAttributeAndValue(String attribute, String value);}

0 0