org.springframework.jdbc.object
пакеті дерекқорға объектке бағдарланған түрде қол жетімділік беретін кластарды қамтиды. Мысалы, сұраныстар орындап, нәтижелерді бизнес-объекттер тізімі ретінде алуға болады, мұнда реляциялық бағандардың деректері бизнес-объектінің қасиеттерімен салыстырылған. Сондай-ақ сақталатын процедураларды іске қосып, жаңарту, жою және қосу нұсқауларын орындай аласыз.
Көптеген Spring әзірлеушілері РСУБД операцияларының әр түрлі кластарын, төменде сипатталған ( StoredProcedure
класын қоспағанда), көбінесе JdbcTemplate
тікелей шақыруларымен ауыстыруға болады деп санайды. DAO әдісін жазу оңайырақ, ол JdbcTemplate
әдісін тікелей шақырады (сұранысты толыққанды класқа инкапсуляциялаудан айырмашылығы).
Алайда, егер сіз РСУБД операцияларының кластарына көбірек пайдалы деп тапсаңыз, осы кластарды пайдалануды жалғастыруыңыз керек.
SqlQuery
туралы негізгі мәліметтер
SqlQuery
– бұл SQL сұранысын инкапсуляциялайтын бірнеше рет қолдануға болатын, потокқа қауіпсіз класс. Подкластары newRowMapper(..)
әдісін жүзеге асыруы қажет, RowMapper
экземплярын ұсыну үшін, ол сұранысты орындау кезінде жасалатын ResultSet
өтіндісінің нәтижесінде алынған әр жол үшін бір объект жасай алады. SqlQuery
класы сирек қолданылады, өйткені MappingSqlQuery
подклассы жолдардың Java класына неғұрлым ыңғайлы көрінісін береді. SqlQuery
кеңейтетін басқа іске асыруларға MappingSqlQueryWithParameters
және UpdatableSqlQuery
жатады.
MappingSqlQuery
қолданылуы
MappingSqlQuery
– бұл бірнеше рет қолдануға болатын сұраныс, онда белгілі подкластар ResultSet
берілген әр жолды көрсетілген типтің объектісіне түрлендіру үшін mapRow(..)
абстракт әдісін жүзеге асыруы қажет. Төмендегі мысалда t_actor қатынасынан деректерді Actor класының экземплярына сәйкестендіретін теңшелген сұраныс көрсетілген:
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")
)
}
Класс Actor
типімен параметрленген MappingSqlQuery
-ді кеңейтеді. Бұл теңшелген сұраныс үшін конструктор DataSource
бір параметр ретінде қабылдайды. Бұл конструкторда суперкласс конструкторын DataSource
және осы сұраныс бойынша жолдарды алу үшін орындалуы қажет SQL шақыруға болады. Бұл SQL датамен PreparedStatement
жасау үшін қолданылады, сондықтан ол орындалу кезінде берілетін кез келген параметрлер үшін орын толтырғыштарды қамтуы мүмкін. declareParameter
әдісін қолданып, SqlParameter
беру арқылы әрбір параметрді жариялау қажет. SqlParameter
аты мен JDBC түрін қабылдайды, java.sql.Types
арқылы анықталған. Барлық параметрлер анықталғаннан кейін, нұсқауды дайындау және одан кейін орындау үшін compile()
әдісін шақыруға болады. Компиляциядан кейін бұл класс потокқа қауіпсіз болып табылады, сондықтан DAO инициализациясында осы экземплярлар жасалса, оларды экземплярлық айнымалылар ретінде сақтап, бірнеше рет пайдалануға болады. Осындай класты қалай анықтау керектігі келесі мысалда көрсетілген:
private ActorMappingQuery actorMappingQuery;
@Autowired
public void setDataSource(DataSource dataSource) {
this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Customer getCustomer(Long id) {
return actorMappingQuery.findObject(id);
}
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id)
Алдыңғы мысалдағы әдіс id
берілген клиентті алады. Тек бір объект қайтару керек болғандықтан, findObject
көмекші әдісін id
параметрімен шақырамыз. Егер керісінше, объектілердің тізімін қайтару және қосымша параметрлерді қабылдайтын сұраныс болса, біз ұзындықтағы аргумент ретінде параметр мәндерінің массивін қабылдайтын execute
әдістерінің бірін қолданар едік. Келесі мысал осындай әдісті көрсетеді:
public List<Actor> searchForActors(int age, String namePattern) {
List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern);
return actors;
}
fun searchForActors(age: Int, namePattern: String) =
actorSearchMappingQuery.execute(age, namePattern)
SqlUpdate
қолданылуы
SqlUpdate
класы SQL жаңартуды инкапсуляциялайды. Сұраныс сияқты, жаңарту объектісі қайта қолдануға болатын, және барлық RdbmsOperation
кластарындағыдай, жаңартуға параметрлер болуы мүмкін және SQL тілінде анықталған. Бұл класс execute(..)
әдістеріне ұқсас бірнеше update(..)
әдістерін ұсынады. SqlUpdate
класы нақты болып табылады. Мысалы, теңшелген жаңарту әдісін қосу үшін оның подкласстарын кеңейтуге болады. Алайда, SqlUpdate
класын кеңейтудің қажеті жоқ, өйткені оны SQL орнату және параметрлерді жариялау арқылы оңай параметрлендіруге болады. Келесі мысалда теңшелген execute
жаңарту әдісі жасалады:
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 жаңартылатын Customer үшін
* @param rating несиелік рейтингтің жаңа мәні
* @return жаңартылған жолдардың саны
*/
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 жаңартылатын Customer үшін
* @param rating несиелік рейтингтің жаңа мәні
* @return жаңартылған жолдардың саны
*/
fun execute(id: Int, rating: Int): Int {
return update(rating, id)
}
}
StoredProcedure
қолданылуы
StoredProcedure
класы РСУБД сақталатын процедураларының объектік абстракциялары үшін abstract
суперкласс болып табылады.
Мұрагерлік қасиет sql
– РСУБД-дегі сақталатын процедураның аты.
StoredProcedure
класы үшін параметрді анықтау үшін SqlParameter
немесе оның біреуінің подкласстарын қолдануға болады. Параметр атауын және SQL түрін конструкторда көрсету қажет, келесі код үзіндісінде көрсетілгендей:
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
және оның подсластары арқылы сұраныстар үшін пайдалануға болады.
Екінші жол ( SqlOutParameter
бірге) out
параметрін жариялайды, ол сақталатын процедура шақыруында қолданылады. InOut
параметрлері үшін SqlInOutParameter
да бар (процедураға in
мәнін беретін, сондай-ақ мәнді қайтаратын параметрлер).
in
параметрлері үшін, SQL түрі мен атауынан басқа, сандық деректер үшін шкаланы немесе дерекқордың арнайы жасалған типтері үшін атауды көрсете аласыз. out
параметрлері үшін курсордың RowMapper
арқылы өңдеуді жасау үшін SqlReturnType
көрсетіп, қайтарылатын мәндерді жеке өңдеуді анықтауға мүмкіндік береді.
Келесі мысалдағы қарапайым DAO StoredProcedure
функциясын шақыру үшін пайдаланылады(sysdate()
), ол кез келген Oracle дерекқорымен бірге беріледі. StoredProcedure функционалдығын пайдалану үшін StoredProcedure
кеңейтетін класс жасау қажет. Бұл мысалда StoredProcedure
класы ішкі класс болып табылады. Дегенмен, егер StoredProcedure қайта қолданғыңыз келсе, оны жоғарғы деңгейдегі класс ретінде жариялай аласыз. Бұл мысалда кіріс параметрлері жоқ, бірақ шығу параметрі SqlOutParameter
арқылы күн түрінде жарияланған. execute()
әдісі процедураны іске қосады және нәтижелік Map
-тан қайтарылған күнді алады. Нәтижелік Map
жарияланған шығу параметрлерінің әрқайсысы үшін жазбаға ие (бұл жағдайда тек біреуінде ғана), параметр атауын кілт ретінде қолдана отырып. Біздің теңшелген StoredProcedure класы келесі листингте көрсетілген:
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() {
// "sysdate" жолының кіріс параметрлері жоқ, сондықтан бос Map шығарылады...
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 {
// "sysdate" жолының кіріс параметрлері жоқ, сондықтан бос Map шығарылады...
val results = execute(mutableMapOf<String, Any>())
return results["date"] as Date
}
}
}
Келесі StoredProcedure
мысалы екі шығу параметрі бар (бұл жағдайда Oracle REF курсорлары):
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() {
// тағы да, бұл сақталатын процедураның кіріс параметрлері жоқ, сондықтан бос Map шығарылады
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> {
// тағы да, бұл сақталатын процедураның кіріс параметрлері жоқ, сондықтан бос Map шығарылады
return super.execute(HashMap<String, Any>())
}
}
declareParameter(..)
әдісінің қайта жүктелген нұсқаларының TitlesAndGenresStoredProcedure
конструкторында қалай қолданылғанын қараңыз, олар RowMapper
іске асыруларының экземплярларына беріледі. Бұл өте ыңғайлы және қолданыстағы функцияларды қайта пайдаланудың тиімді әдісі. Келесі екі мысалда RowMapper
екі іске асырудың коды көрсетілген.
TitleMapper
класы ResultSet
-ді келесі жолмен Title
домендік объектімен әр жолға салыстырады:
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
домендік объектімен әр жолға салыстырады:
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"))
}
}
РСУБД-дегі анықтамасында бір немесе бірнеше кіріс параметрлері бар сақталатын процедураға параметрлерді беру үшін, келесі мысалда көрсетілгендей, қатты типтелген execute(..)
әдісін жазуға болады, ол суперкласстағы типтелмеген execute(Map)
әдісіне делегат болады:
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))
}
GO TO FULL VERSION