Part I Core JDBC Extensions
1 扩展Spring特性的一些支持类
Spring 框架中JDBC支持是很好,但是时不时有一些功能看起来有用可是不包含在框架中。Spring Data JDBC Extensions 项目为这些类型的扩展提供了一个 “Home”。
1.1 一对多关系映射
我们常常会在我们的数据库项目中使用一对多的关系型映射。一个顾客会有多个地址,一个订单可能会包含多项等等。我们现在提供一个ResultSetExtractor 实现来处理这些公共的工作。
让我们来看看下面定义好的数据库结构
CREATE TABLE customer(
id BIGINT IDENTITY PRIMARY KEY,
name VARCHAR(255));
CREATE TABLE address (
id BIGINT IDENTITY PRIMARY KEY,
customer_id BIGINT CONSTRAINT address_customer_ref
FOREIGN KEY REFERENCES customer (id),
street VARCHAR(255),
city VARCHAR(255));
上面两张表是通过一个外键约束关联起来的。在具体实现类 - Customer 和Address 中, Customer 将有一个Address集合来映射这种关系。
public class Customer {
private Integer id;
private String name;
private Set<Address> addresses = new HashSet<Address>();
public Set<Address> getAddresses() {
return addresses;
}
public void addAddress(Address address) {
this.addresses.add(address);
}
// other setters and getters
}
public class Address {
private Integer id;
private String street;
private String city;
// setters and getters
}
执行下面查询语句,对于每个Customer 可能会有多条记录返回。
List<Customer> result = template.query(
"select customer.id, customer.name, address.id, " +
"address.customer_id, address.street, address.city " +
"from customer " +
"left join address on customer.id = address.customer_id " +
"order by customer.id",
resultSetExtractor);
为了能够获得多行记录,我们通过继承OneToManyResultSetExtractor来创建一个新的类CustomerAddressExtractor。 通过root class(Customer), child class(Address)以及主外键来参数化OneToManyResultSetExtractor。
public class CustomerAddressExtractor extends
OneToManyResultSetExtractor<Customer, Address, Integer> {
public CustomerAddressExtractor() {
super(new CustomerMapper(), new AddressMapper());
}
@Override
protected Integer mapPrimaryKey(ResultSet rs) throws SQLException {
return rs.getInt("customer.id");
}
@Override
protected Integer mapForeignKey(ResultSet rs) throws SQLException {
if (rs.getObject("address.customer_id") == null) {
return null;
}
else {
return rs.getInt("address.customer_id");
}
}
@Override
protected void addChild(Customer root, Address child) {
root.addAddress(child);
}
}
我们需要一种方式来匹配Customer的主键与Address的外键。因此我们使用一种抽象的方法mapPrimaryKey 和mapForeignKey来映射这种关系。我们必须考虑对于Customer他的地址可能不存在,所以Customer 外键可能为空。我们同样需要把Address 对象映射到Customer中。通过实现一个抽象的方法addChild,可以在Customer 类中调用Address。
在CustomerAddressExtractor的构造方法中,我们调用父类构造方法为Customer 和 Address 实现RowMapper接口。 在本例中,这些标准的标准的RowMappers是通过内部类方式来实现的。
private static class CustomerMapper implements RowMapper<Customer> {
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer c = new Customer();
c.setId(rs.getInt("customer.id"));
c.setName(rs.getString("customer.name"));
return c;
}
}
private static class AddressMapper implements RowMapper<Address> {
public Address mapRow(ResultSet rs, int rowNum) throws SQLException {
Address a = new Address();
a.setId(rs.getInt("address.id"));
a.setStreet(rs.getString("address.street"));
a.setCity(rs.getString("address.city"));
return a;
}
}