Spring实战学习笔记 —— 2.使用数据
1. 使用JDBC读取和写入数据
Spring同时支持JDBC和JPA两种抽象形式,Spring对于JDBC的支持要归功于JdbcTemplate类(Spring Data JDBC)。
如果在应用的根类路径下存在名为schema.sql和data.sql的文件,那么在应用启动的时候将会基于数据库执行文件中的SQL。
1.1 Spring Data JDBC
Spring Data JDBC是将传统数据库查询进行封装,以queryForObject为例。
@Autowired
JdbcTemplate jdbc;
public Order findOne(String id) {
return jdbc.queryForObject("select * from order where id = ?", this::mapRowToOrder, id);
}
private Order mapRowToOrder(ResultSet rs, int rowNum) {
return new Order(rs.getString("id"), rs.getString("code")....);
}
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
// 方法入口,RowMapper实体类处理器
@Nullable
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = this.query(sql, rowMapper);
return DataAccessUtils.nullableSingleResult(results);
}
// 返回校验
@Nullable
public static <T> T nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
// 查询入口,RowMapperResultSetExtractor行数据提取器,用于提取多行数据
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return (List)result(this.query((String)sql, (ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper))));
}
// 通过构造回调类,使查询后进入行数据提取器从而根据不同的类构造不同的实体类
@Nullable
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (this.logger.isDebugEnabled()) {
this.logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
QueryStatementCallback() {
}
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
Object var3;
try {
rs = stmt.executeQuery(sql);
var3 = rse.extractData(rs);
} finally {
JdbcUtils.closeResultSet(rs);
}
return var3;
}
public String getSql() {
return sql;
}
}
return this.execute((StatementCallback)(new QueryStatementCallback()));
}
// jdbc开启查询,也是传统jdbc代码
@Nullable
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
Statement stmt = null;
Object var11;
try {
stmt = con.createStatement();
this.applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
this.handleWarnings(stmt);
var11 = result;
} catch (SQLException var9) {
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, this.getDataSource());
con = null;
throw this.translateException("StatementCallback", sql, var9);
} finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, this.getDataSource());
}
return var11;
}
}
对于增删改,可以使用
// 自增主键
PreparedStatementCreaetor psc = new PreparedStatementCreatorFactory("insert into order(code) valus (?)",
Types.BIGINT, Types.VARCHAR)
.newPreparedStatementCreator(Arrays.asList(order.getCode()));
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbc.update(psc, key Holder);
return keyHolder.getKey().longValue();
或
jdbc.update("insert into order(id, code) valus (?, ?)", order.getId(), order.getCode());
1.2 SimpleJdbcInsert
@Repository
public class JdbcOrderRepository {
private SimpleJdbcInsert orderInsertGenerated;
private SimpleJdbcInsert orderInsert;
private ObjectMapper objectMapper;
@Autowired
public JdbcOrderRepository(JdbcTemplate jdbc) {
this.orderInsertGenerated = new SimpleJdbcInsert(jdbc).withTableName("order").usingGeneratedKeyColumns("id");
this.orderInsert = new SimpleJdbcInsert(jdbc).withTableName("order");
this.objectMapper = new ObjectMapper;
}
public Long save(Order order) {
long id = orderInsertGenerated.executeAndReturnKey(objectMapper.convertValue(order, Map.class)).longValue();
return id;
}
public void save(Order order, long id) {
Map<String, Object> values = new HashMap<String, Object>();
values.put("id", id);
values.put("code", code);
orderInsert.execute(values);
}
}
2.使用JPA持久化数据
2.1 实体类处理
@Entity
声明在类名上,表示为JPA实体
@Table(name=“table name”)
声明在类名上,表示对应的数据表名称
@Id
声明在属性上,表示为数据库主键
@GeneratedValue(strategy=GenerationType.AUTO)
声明在属性上,表示为自增属性
@ManyToMany
声明在其他实体类对象或集合上,表示与其他实体类的对应关系为多对多
@PrePersist
声明在方法上,表示在持久化到数据库前需要执行的方法
2.2 声明JPA Repository
public interface OrderRepository extends CrudRepository<Order, Long> {
}
通过继承CrudRepository,并指定实体类与主键类型,不需要实现具体方法即可使用CrudRepository中自带的方法,运行器JPA会自动生成实现类。
同时,可以通过Spring Data方法签名来让JPA生成更多的我们需要的方法,如:
List<Order> readOrdersByCodeAndIdBetweenOrderById(String code, long startId, long endId);
查找Order表是因为我们在CrudRepository中声明了实体类,实体类中声明了对应表
read:读取数据,可用get或find替换
By:开始声明要匹配的属性
Code:匹配属性
And:同SQL中的and
Id:匹配属性
Between:值必须在给定的范围内
OrderBy: 排序
还有更多的方法签名可以自行查阅
或者
@Query("from order where code = ‘123’")
List<Order> readOrdersByCode()