JavaRush /Курстар /All lectures for KK purposes /JDBC операцияларын Java объектілері ретінде модельдеу

JDBC операцияларын Java объектілері ретінде модельдеу

All lectures for KK purposes
Деңгей , Сабақ
Қол жетімді

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 класының экземплярына сәйкестендіретін теңшелген сұраныс көрсетілген:

Java
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;
    }
}
Kotlin
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 инициализациясында осы экземплярлар жасалса, оларды экземплярлық айнымалылар ретінде сақтап, бірнеше рет пайдалануға болады. Осындай класты қалай анықтау керектігі келесі мысалда көрсетілген:

Java
private ActorMappingQuery actorMappingQuery;
@Autowired
public void setDataSource(DataSource dataSource) {
    this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Customer getCustomer(Long id) {
    return actorMappingQuery.findObject(id);
}
Kotlin
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id)

Алдыңғы мысалдағы әдіс id берілген клиентті алады. Тек бір объект қайтару керек болғандықтан, findObject көмекші әдісін id параметрімен шақырамыз. Егер керісінше, объектілердің тізімін қайтару және қосымша параметрлерді қабылдайтын сұраныс болса, біз ұзындықтағы аргумент ретінде параметр мәндерінің массивін қабылдайтын execute әдістерінің бірін қолданар едік. Келесі мысал осындай әдісті көрсетеді:

Java
public List<Actor> searchForActors(int age, String namePattern) {
    List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern);
    return actors;
}
Kotlin
fun searchForActors(age: Int, namePattern: String) =
            actorSearchMappingQuery.execute(age, namePattern)

SqlUpdate қолданылуы

SqlUpdate класы SQL жаңартуды инкапсуляциялайды. Сұраныс сияқты, жаңарту объектісі қайта қолдануға болатын, және барлық RdbmsOperation кластарындағыдай, жаңартуға параметрлер болуы мүмкін және SQL тілінде анықталған. Бұл класс execute(..) әдістеріне ұқсас бірнеше update(..) әдістерін ұсынады. SqlUpdate класы нақты болып табылады. Мысалы, теңшелген жаңарту әдісін қосу үшін оның подкласстарын кеңейтуге болады. Алайда, SqlUpdate класын кеңейтудің қажеті жоқ, өйткені оны SQL орнату және параметрлерді жариялау арқылы оңай параметрлендіруге болады. Келесі мысалда теңшелген execute жаңарту әдісі жасалады:

Java
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);
    }
}
Kotlin
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 түрін конструкторда көрсету қажет, келесі код үзіндісінде көрсетілгендей:

Java
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
Kotlin
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 класы келесі листингте көрсетілген:

Java
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;
        }
    }
}
Kotlin
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 курсорлары):

Java
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>());
    }
}
Kotlin
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 домендік объектімен әр жолға салыстырады:

Java
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;
    }
}
Kotlin
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 домендік объектімен әр жолға салыстырады:

Java
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"));
    }
}
Kotlin
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) әдісіне делегат болады:

Java
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);
    }
}
Kotlin
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))
}
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION