Spring in action 3.使用数据

205 阅读3分钟

使用JDBC读取和写入数据

调整领域对象以适应持久化

将对象持久化到数据库的时候,通常最好有一个字段作为对象的唯一标识(ID)。

使用JDBCtemplate

定义模式和预加载数据

插入数据

使用Spring Data JPA持久化数据

一些其他的Spring Data项目,Spring Data为所有项目提供了一项最有趣且最有用的特性,就是基于repository规范接口自动生成repository的功能。

  • Spring Data JPA:基于关系型数据库进行JPA持久化。
  • Spring Data MongoDB:持久化到Mongo文档数据库。
  • Spring Data Neo4j:持久化到Neo4j图数据库。
  • Spring Data Redis:持久化到Redis key-value存储。
  • Spring Data Cassandra:持久化到cassandra数据库。

添加Spring Data JPA到项目中

引入 spring-boot-starter-data-jpa

将领域对象标注为实体

@Data
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE,force = true)
@Entity
public class Ingredient {

    @Id
    private final String id;
    private final String name;
    private final Type type;

    public static enum Type {
        WRAP,PROTEIN,VEGGIES,CHEESE,SAUCE
    }
}
  • @Entity:用@Entity来将Ingredient声明为JPA实体,它的id用@ID注解将其指定为数据库中唯一标识该实实体店属性。
  • @NoArgsConstructor:Lombok的NoArgsConstructor为JPA实体提供了一个无参构造器,但是我们不想直接使用它,因此通过access属性设置为AccessLevel.PRIVATE使其变成私有的。因为这里有必须要设置的final属性,所以我们将force设置为true,这样lombok的构造器就会将它们设置为null。
  • @RequiredArgsConstructor:@Data注解会为我们生成一个有参构造器,但是使用@NoArgsConstructor注解之后,这个有参构造器会被移除,所以现在显式的使用@RequiredArgsConstructor注解,以确保除了private的无参构造器之外,还会有一个有参构造器。
@Data
@Entity
public class Taco {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private Date createdAt;

  @NotNull
  @Size(min=5, message="Name must be at least 5 characters long")
  private String name;
  
  @ManyToMany
  @Size(min=1, message="You must choose at least 1 ingredient")
  private List<Ingredient> ingredients;
  
  @PrePersist
  void createdAt(){
    this.createdAt = new Date();
  }
}
  • @Entity:为Id添加注解@ID和@GeneratedValue,使其id能自动生成。
  • @ManyToMany:用来指声明两个JPA实体之间多对多的关系。
  • @PrePersist:在Taco实体持久化之前,调用被修饰的createdAt()方法。
@Data
@Entity
@Table(name = "Taco_Order")
public class Order implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private Date placedAt;

  ...
  ...

  @ManyToMany(targetEntity = Taco.class)
  private List<Taco> tacos = new ArrayList<>();
  

  @PrePersist
  void placedAt(){
    this.placedAt = new Date();
  }
}
  • @Table:它表明要将Order实体类持久化到数据库中名为Taco_Order的表中。

声明JPA repository

public interface IngredientRepository extends CrudRepository<Ingredient,String> {

}
  • CrudRepository:该接口定义了很多用于CRUD操作的方法,它是参数化的,第一个参数是要持久化的实体类型,第二个是实体ID属性的类型。
  • Spring Data JPA带来的好处是,我们根本不用编写实现类,当应用启动时,SpringDATAJPA会在运行期间自动生成实现类

自定义JPA repository

  • 按照Zip查找订单
    • List< Order >findByDeliveryZip(String deliveryZip);
  • 指定时间内按Zip查找
    • List< Order >readOrdersByDeliveryZipAndPlacedAtBetween(String deliveryZip,Date startDate,Date endDate);
    • get,read,find同义。
    • 用count作动词,可以返回匹配实体的数量。
  • 忽略所有String的大小写
    • List< Order >findByDeliveryToAndDeliveryCityAllIgnoresCase(String deliveryTo,String deliveryCity);
  • 结果集根据某个列排序
    • List< Order >findByDeliveryCityOrderByDeliveryTo(String city);
  • 自定义,用@Query注解
    • @Query("Order o where o.deliveryCity='Seattle'")
    • List< Order > readOrdersDeliveryInSeattle();
  • 参考

小结

  • Spring的jdbdTeplate能够极大的简化JDBC的使用
  • 在我们需要知道数据库所生成的ID值时,可以组合使用PreparedStatementCreator和KeyHolder。
  • 为了简化数据的插入,我们可以使用SimpleJdbcInsert。
  • Spring Data JPA能够极大的简化JPA持久化,我们只需编写repository接口即可。