Spring Data JDBC一对一和一对多关系实现

  Spring Data JDBC能够支持DDD中聚合概念,实际支持ER中星型模型,在DDD中聚合根实体代表整体概念,映射到数据表中就是星型模型中的主表,整体和部分的关系一般是一对一和一对多关系,其中一代表整体这一方,不会存在多对一或多对多关系,因为整体只能有一个。

1:1关系

首先看看一对一关系,一对一关系体现在对象和数据表两个方面,先看看你对象的一对一关系,假设有一个Order订单,其中包含了对地址Address的一对一关系,Order代码如下:


@Table("ordersample")
public class Order {
@org.springframework.data.annotation.Id
public Integer Id;
//代表一对一的关系
public Address address;

..

}

Address的代码如下:
public class Address {
   @org.springframework.data.annotation.Id
public Integer Id;
public String street;
public String zipcode;
}

一对一关系中,对象的类中都必须有一个主键标识,如上面的Id,Order和Address都有一个唯一标识Id,注释都是@org.springframework.data.annotation.Id,注意不是javax.persistence.Id,后者是JPA的主键标识。

 

那么,对应的数据表结构应该怎样呢?我们可以从运行出错的信息中一窥究竟。

先把仓储接口写好:
public interface OrderRepo extends CrudRepository<Order, Integer>

再写好测试代码:

Order order = new Order();

Address address = new Address();
address.street = "a street";
address.zipcode = "123456789";
order.address = address;

orderRepo.save(order);

 

试验运行的时候会提示SQL出错,数据表结构没有创建:

Table "ORDERSAMPLE" not found; SQL statement:
INSERT INTO ordersample () VALUES () [42102-197]

Order对应的数据表ordersample没有创建,注意这里数据表名称如果不指定ordersample,默认是order会引起冲突,order是保留名称。

ordersample数据表主要是一个自增主键:

CREATE TABLE ordersample (
id Integer IDENTITY PRIMARY KEY,
);

在application.properties填入配置:

logging.level.sql=true
database=h2
spring.datasource.schema=classpath*:schema.sql
spring.h2.console.enabled=Debug

这里需要导入schema.sql用来创建数据表结构。将ordersample数据表结构SQL放入schema.sql即可。

再次运行测试程序:

Caused by: org.h2.jdbc.JdbcSQLException: Table "ADDRESS" not found; SQL statement:
INSERT INTO address (street, zipcode, ordersample) VALUES (?, ?, ?) [42102-197]

这里SQL语句已经显示Address对应的address数据表结构:

CREATE TABLE address (
id Integer IDENTITY PRIMARY KEY,
ordersample Integer,
street VARCHAR(80),
zipcode VARCHAR(80)
);

这里的id对应类Address中@Id标注的主键,street和zipcode也是对应类Address的字段,比较特殊的是ordersample,这个显然是一个代表关系的外键,指向ordersample数据表,也就是说,数据表之间表达一对一关系的方式是通过外键引用实现的。

1:N关系

假设订单Order中存在OrderItem:

@Table("ordersample")
public class Order {
@org.springframework.data.annotation.Id
public Integer Id;
//1:1
public Address address;
//1:N
public Collection<OrderItem> items;

类Order中使用集合Collection表示多个OrderItem,OrderItem的代码:


public class OrderItem {
public String productVO;
public int qty;

}

注意到,在1:N关系中,多方这里无需使用@Id唯一主键,从DDD角度来看,OrderItem其实是一个值对象,值对象与实体的区别就是没有唯一标识。OrderItem对应的数据表结构:

CREATE TABLE order_item (
ordersample_key Integer IDENTITY PRIMARY KEY,
product_vo VARCHAR(80),
qty INTEGER,
ordersample INTEGER
);

我们注意到,Spring Data JDBC默认使用ordersample_key作为order_item表的主键,因为OrderItem对象是值对象,但是数据表需要主键,因此使用ordersample_key,同时外键ordersample指向同名的主表ordersample,这是数据表的1:N的方式。

源码

Spring Data JDBC主题