初识Spring Data与JPA Repository

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的同种类型的实体会

被视为相等。

@MappedSuperclass
public 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注解

@Entity
public 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注解,会导致持久化供应商会将其属性放到包含它的类所对应的表中

@Embeddable
public 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,从而实现表的关联

@Entity
public 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,它会用来存储额外的属性,不同产品之间这些属性有可能不同

@Entity
public 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

@Entity
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值