此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
将 JDBC作建模为 Java 对象
这org.springframework.jdbc.object
package 包含允许您访问的类
数据库以更加面向对象的方式。例如,您可以运行查询
并将结果作为列表返回,其中包含具有关系
映射到业务对象属性的列数据。您还可以运行存储
过程并运行 update、delete 和 insert 语句。
许多 Spring 开发人员认为,下面描述的各种 RDBMS作类
(除了 但是,如果您通过使用 RDBMS作类获得可衡量的价值, 您应该继续使用这些类。 |
理解SqlQuery
SqlQuery
是一个可重用的线程安全类,用于封装 SQL 查询。子
必须实现newRowMapper(..)
方法提供RowMapper
实例可以
通过迭代获得的每行创建一个对象ResultSet
创建的
在查询执行期间。这SqlQuery
class 很少直接使用,因为
这MappingSqlQuery
子类为
将行映射到 Java 类。扩展的其他实现SqlQuery
是MappingSqlQueryWithParameters
和UpdatableSqlQuery
.
用MappingSqlQuery
MappingSqlQuery
是一个可重用的查询,其中具体子类必须实现
抽象mapRow(..)
转换所提供ResultSet
变成一个
指定类型的对象。以下示例显示了一个自定义查询,该查询映射了
数据来自t_actor
与Actor
类:
-
Java
-
Kotlin
public class ActorMappingQuery extends MappingSqlQuery<Actor> {
public ActorMappingQuery(DataSource ds) {
super(ds, "select id, first_name, last_name from t_actor where id = ?");
declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
@Override
protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
}
class ActorMappingQuery(ds: DataSource) : MappingSqlQuery<Actor>(ds, "select id, first_name, last_name from t_actor where id = ?") {
init {
declareParameter(SqlParameter("id", Types.INTEGER))
compile()
}
override fun mapRow(rs: ResultSet, rowNumber: Int) = Actor(
rs.getLong("id"),
rs.getString("first_name"),
rs.getString("last_name")
)
}
该类扩展MappingSqlQuery
参数化为Actor
类型。构造函数
对于此客户查询,查询采用DataSource
作为唯一参数。在这个
constructor,您可以使用DataSource
和 SQL
应运行该命令以检索此查询的行。此 SQL 用于
创建一个PreparedStatement
,因此它可能包含任何参数的占位符
在执行过程中传入。您必须使用declareParameter
方法传入SqlParameter
.这SqlParameter
采用名称,JDBC 类型
如java.sql.Types
.定义所有参数后,可以调用compile()
方法,以便可以准备语句并稍后运行。这个类是
编译后线程安全,因此,只要在 DAO
初始化时,它们可以保留为实例变量并重用。以下内容
示例显示了如何定义这样的类:
-
Java
-
Kotlin
private ActorMappingQuery actorMappingQuery;
@Autowired
public void setDataSource(DataSource dataSource) {
this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Actor getActor(Long id) {
return actorMappingQuery.findObject(id);
}
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getActor(id: Long) = actorMappingQuery.findObject(id)
前面示例中的方法检索具有id
作为
only 参数。由于我们只希望返回一个对象,因此我们调用findObject
方便
方法与id
作为参数。如果我们有一个返回
列表并采用其他参数,我们将使用execute
接受作为 varargs 传递的参数值数组的方法。以下内容
示例显示了这样的方法:
-
Java
-
Kotlin
public List<Actor> searchForActors(int age, String namePattern) {
return actorSearchMappingQuery.execute(age, namePattern);
}
fun searchForActors(age: Int, namePattern: String) =
actorSearchMappingQuery.execute(age, namePattern)
用SqlUpdate
这SqlUpdate
class 封装 SQL 更新。与查询一样,更新对象是
可重用,并且,与所有RdbmsOperation
类,更新可以有参数,并且是
在 SQL 中定义。此类提供了许多update(..)
类似于execute(..)
查询对象的方法。这SqlUpdate
class 是具体的。它可以是
subclassed — 例如,添加自定义更新方法。
但是,您不必将SqlUpdate
类,因为它可以通过设置 SQL 和声明参数轻松参数化。
以下示例创建一个名为execute
:
-
Java
-
Kotlin
import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
public class UpdateCreditRating extends SqlUpdate {
public UpdateCreditRating(DataSource ds) {
setDataSource(ds);
setSql("update customer set credit_rating = ? where id = ?");
declareParameter(new SqlParameter("creditRating", Types.NUMERIC));
declareParameter(new SqlParameter("id", Types.NUMERIC));
compile();
}
/**
* @param id for the Customer to be updated
* @param rating the new value for credit rating
* @return number of rows updated
*/
public int execute(int id, int rating) {
return update(rating, id);
}
}
import java.sql.Types
import javax.sql.DataSource
import org.springframework.jdbc.core.SqlParameter
import org.springframework.jdbc.object
.SqlUpdate
class UpdateCreditRating(ds: DataSource) : SqlUpdate() {
init {
setDataSource(ds)
sql = "update customer set credit_rating = ? where id = ?"
declareParameter(SqlParameter("creditRating", Types.NUMERIC))
declareParameter(SqlParameter("id", Types.NUMERIC))
compile()
}
/**
* @param id for the Customer to be updated
* @param rating the new value for credit rating
* @return number of rows updated
*/
fun execute(id: Int, rating: Int): Int {
return update(rating, id)
}
}
用StoredProcedure
这StoredProcedure
class 是一个abstract
用于 RDBMS 对象抽象的超类
存储过程。
继承的sql
属性是 RDBMS 中存储过程的名称。
要定义参数StoredProcedure
类,您可以使用SqlParameter
或一个
其子类。您必须在构造函数中指定参数名称和 SQL 类型,
如以下代码片段所示:
-
Java
-
Kotlin
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),
SQL 类型是使用java.sql.Types
常数。
第一行(使用SqlParameter
) 声明一个 IN 参数。您可以使用 IN 参数
对于存储过程调用和使用SqlQuery
及其
子类(涵盖在理解SqlQuery
).
第二行(带有SqlOutParameter
) 声明out
参数用于
存储过程调用。还有一个SqlInOutParameter
为InOut
参数
(提供in
value 添加到过程中,并且也返回一个值)。
为in
参数,除了名称和 SQL 类型之外,您还可以指定一个
数值数据的缩放或自定义数据库类型的类型名称。为out
参数
你可以提供一个RowMapper
处理从REF
光标。
另一种选择是指定SqlReturnType
允许您定义自定义
返回值的处理。
简单 DAO 的下一个示例使用StoredProcedure
调用函数
(sysdate()
),它随任何 Oracle 数据库一起提供。使用存储过程
功能,您必须创建一个扩展StoredProcedure
.在这个
示例,StoredProcedure
class 是一个内部类。但是,如果您需要重用StoredProcedure
,您可以将其声明为顶级类。此示例没有输入
参数,但输出参数通过使用SqlOutParameter
类。这execute()
方法运行该过程并提取
从结果返回的日期Map
.结果Map
每个声明的条目都有一个条目
输出参数(在本例中仅使用一个)使用参数名称作为键。
以下列表显示了我们的自定义 StoredProcedure 类:
-
Java
-
Kotlin
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class StoredProcedureDao {
private GetSysdateProcedure getSysdate;
@Autowired
public void init(DataSource dataSource) {
this.getSysdate = new GetSysdateProcedure(dataSource);
}
public Date getSysdate() {
return getSysdate.execute();
}
private class GetSysdateProcedure extends StoredProcedure {
private static final String SQL = "sysdate";
public GetSysdateProcedure(DataSource dataSource) {
setDataSource(dataSource);
setFunction(true);
setSql(SQL);
declareParameter(new SqlOutParameter("date", Types.DATE));
compile();
}
public Date execute() {
// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...
Map<String, Object> results = execute(new HashMap<String, Object>());
Date sysdate = (Date) results.get("date");
return sysdate;
}
}
}
import java.sql.Types
import java.util.Date
import java.util.Map
import javax.sql.DataSource
import org.springframework.jdbc.core.SqlOutParameter
import org.springframework.jdbc.object.StoredProcedure
class StoredProcedureDao(dataSource: DataSource) {
private val SQL = "sysdate"
private val getSysdate = GetSysdateProcedure(dataSource)
val sysdate: Date
get() = getSysdate.execute()
private inner class GetSysdateProcedure(dataSource: DataSource) : StoredProcedure() {
init {
setDataSource(dataSource)
isFunction = true
sql = SQL
declareParameter(SqlOutParameter("date", Types.DATE))
compile()
}
fun execute(): Date {
// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...
val results = execute(mutableMapOf<String, Any>())
return results["date"] as Date
}
}
}
以下示例StoredProcedure
有两个输出参数(在本例中,
Oracle REF 游标):
-
Java
-
Kotlin
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import oracle.jdbc.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class TitlesAndGenresStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "AllTitlesAndGenres";
public TitlesAndGenresStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));
compile();
}
public Map<String, Object> execute() {
// again, this sproc has no input parameters, so an empty Map is supplied
return super.execute(new HashMap<String, Object>());
}
}
import java.util.HashMap
import javax.sql.DataSource
import oracle.jdbc.OracleTypes
import org.springframework.jdbc.core.SqlOutParameter
import org.springframework.jdbc.object
.StoredProcedure
class TitlesAndGenresStoredProcedure(dataSource: DataSource) : StoredProcedure(dataSource, SPROC_NAME) {
companion object {
private const val SPROC_NAME = "AllTitlesAndGenres"
}
init {
declareParameter(SqlOutParameter("titles", OracleTypes.CURSOR, TitleMapper()))
declareParameter(SqlOutParameter("genres", OracleTypes.CURSOR, GenreMapper()))
compile()
}
fun execute(): Map<String, Any> {
// again, this sproc has no input parameters, so an empty Map is supplied
return super.execute(HashMap<String, Any>())
}
}
请注意declareParameter(..)
方法已经
用于TitlesAndGenresStoredProcedure
构造函数被传递RowMapper
实现实例。这是一种非常方便且强大的重用现有方法
功能性。接下来的两个示例提供了两个RowMapper
实现。
这TitleMapper
类映射ResultSet
设置为Title
domain 中每一行的对象
提供的ResultSet
如下:
-
Java
-
Kotlin
import java.sql.ResultSet;
import java.sql.SQLException;
import com.foo.domain.Title;
import org.springframework.jdbc.core.RowMapper;
public final class TitleMapper implements RowMapper<Title> {
public Title mapRow(ResultSet rs, int rowNum) throws SQLException {
Title title = new Title();
title.setId(rs.getLong("id"));
title.setName(rs.getString("name"));
return title;
}
}
import java.sql.ResultSet
import com.foo.domain.Title
import org.springframework.jdbc.core.RowMapper
class TitleMapper : RowMapper<Title> {
override fun mapRow(rs: ResultSet, rowNum: Int) =
Title(rs.getLong("id"), rs.getString("name"))
}
这GenreMapper
类映射ResultSet
设置为Genre
domain 中每一行的对象
提供的ResultSet
如下:
-
Java
-
Kotlin
import java.sql.ResultSet;
import java.sql.SQLException;
import com.foo.domain.Genre;
import org.springframework.jdbc.core.RowMapper;
public final class GenreMapper implements RowMapper<Genre> {
public Genre mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Genre(rs.getString("name"));
}
}
import java.sql.ResultSet
import com.foo.domain.Genre
import org.springframework.jdbc.core.RowMapper
class GenreMapper : RowMapper<Genre> {
override fun mapRow(rs: ResultSet, rowNum: Int): Genre {
return Genre(rs.getString("name"))
}
}
将参数传递给具有一个或多个输入参数的存储过程
RDBMS 中的定义,您可以编写一个强类型的execute(..)
方法将
委托给非类型execute(Map)
方法,如以下示例所示:
-
Java
-
Kotlin
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import oracle.jdbc.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class TitlesAfterDateStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "TitlesAfterDate";
private static final String CUTOFF_DATE_PARAM = "cutoffDate";
public TitlesAfterDateStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
compile();
}
public Map<String, Object> execute(Date cutoffDate) {
Map<String, Object> inputs = new HashMap<String, Object>();
inputs.put(CUTOFF_DATE_PARAM, cutoffDate);
return super.execute(inputs);
}
}
import java.sql.Types
import java.util.Date
import javax.sql.DataSource
import oracle.jdbc.OracleTypes
import org.springframework.jdbc.core.SqlOutParameter
import org.springframework.jdbc.core.SqlParameter
import org.springframework.jdbc.object
.StoredProcedure
class TitlesAfterDateStoredProcedure(dataSource: DataSource) : StoredProcedure(dataSource, SPROC_NAME) {
companion object {
private const val SPROC_NAME = "TitlesAfterDate"
private const val CUTOFF_DATE_PARAM = "cutoffDate"
}
init {
declareParameter(SqlParameter(CUTOFF_DATE_PARAM, Types.DATE))
declareParameter(SqlOutParameter("titles", OracleTypes.CURSOR, TitleMapper()))
compile()
}
fun execute(cutoffDate: Date) = super.execute(
mapOf<String, Any>(CUTOFF_DATE_PARAM to cutoffDate))
}