Настройка системы
В предыдущей статье я упомянул об использовании MyBatis-Guice. В этой статье я покажу способ настройки Guice модуля.Для начала напишем базовые xml файлы настройки MyBatis. Можно было бы обойтись и без них, но мне не очень нравится избыточное количество аннотаций каждого метода.
mapping.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <typeAlias type="org.myproject.model.ReceiptDoc" alias="ReceiptDoc"/> <typeAlias type="org.myproject.impl.model.entity.ReceiptDocEntity" alias="ReceiptDocEntity"/> </typeAliases> <mappers> <mapper resource="org/myproject/db/ReceiptDocMapper.xml"/> </mappers> </configuration>Файл с SQL запросами
org/myproject/db/ReceiptDocMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.myproject.impl.provider.ReceiptDocMapper"> <resultMap id="docMap" type="ReceiptDocEntity"> <id property="id" column="ID" jdbcType="VARCHAR"/> <result property="dateLoad" column="DATELOAD" jdbcType="TIMESTAMP"/> <result property="docUrl" column="DOC_URL" jdbcType="VARCHAR"/> <result property="docName" column="DOC_NAME" jdbcType="VARCHAR"/> <result property="fileName" column="FILE_NAME" jdbcType="VARCHAR"/> </resultMap> <!-- Поиск документа по коду --> <select id="findReceiptDoc" resultMap="docMap" parameterType="string"> select ID, DATELOAD, DOC_URL, DOC_NAME, FILE_NAME from RECEIPT_DOC where ID = #{id} </select> <!-- Поиск документа по названию --> <select id="findReceiptDocByName" resultMap="docMap" parameterType="string"> select ID, DATELOAD, DOC_URL, DOC_NAME, FILE_NAME from RECEIPT_DOC where DOC_NAME = #{name} </select> <!-- Добавление нового документа --> <insert id="insert" parameterType="ReceiptDoc"> insert into RECEIPT_DOC ( ID, DATELOAD, DOC_URL, DOC_NAME, FILE_NAME ) values ( #{id, jdbcType=VARCHAR}, #{dateLoad, jdbcType=TIMESTAMP}, #{docUrl, jdbcType=VARCHAR}, #{docName, jdbcType=VARCHAR}, #{fileName, jdbcType=VARCHAR} ) </insert> </mapper>
Далее, составим модель объектов.
ReceiptDoc - это интерфейс сохраняемого объекта.
ReceiptDocEntity - это сохраняемый объект.
ReceiptDocMapper - это интерфейс генерируемого объекта, за которым MyBatis прячет весь функционал
взаимодействия с базой данных.
Интерфейс манипуляции с базой данных ReceiptDocMapper
package org.myproject.impl.provider; import org.myproject.model.ReceiptDoc; public interface ReceiptDocMapper { ReceiptDoc findReceiptDoc(String id); List<ReceiptDoc> findReceiptDocByName(String name); void insert(ReceiptDoc receiptDoc); }
Интерфейс данных ReceiptDoc
package org.myproject.model; import java.util.Date; public interface ReceiptDoc { String getId(); void setId(String id); Date getDateLoad(); void setDateLoad(Date dateLoad); String getDocUrl(); void setDocUrl(String docUrl); String getDocName(); void setDocName(String docName); String getFileName(); voi setFileName(String fileName); }
Сохраняемый объект ReceiptDocEntity
package org.myproject.impl.model.entity; import java.util.Date; import org.myproject.model.ReceiptDoc; public class ReceiptDocEntity implements ReceiptDoc, Serializable { private static final long serialVersionUID = 1L; // Далее идет реализация интерфейса ReceiptDoc }
Теперь у нас есть данные для хранения, интерфейс поиска и добавления записей в базу данных. Так же, есть файлы настроек системы MyBatis. Далее, нам понадобится объект, методы которого будут работать с использованием транзакций.
Для этого создадим новый интерфейс объекта, который, для простоты, будет реализовывать те же методы, что и ранее представленный интерфейс ReceiptDocMapper.
package org.myproject.provider; import org.apache.ibatis.session.SqlSessionFactory; import org.myproject.model.ReceiptDoc; public interface Provider { ReceiptDoc findReceiptDoc(String id); List<ReceiptDoc> findReceiptDocByName(String name); void insert(ReceiptDoc receiptDoc); SqlSessionFactory getSqlSessionFactory(); }Теперь напишем реализацию данного интерфейса.
Реализация одного из методов интерфейса Provider могла бы выглядеть примерно так:
private SqlSessionManager sqlSessionManager; @Override public ReceiptDoc findReceiptDoc(String id) { boolean isSessionInherited = sqlSessionManager.isManagedSessionStarted(); if(!isSessionInherited) { sqlSessionManager.startManagedSession(); } try { ReceiptDocMapper mapper = sqlSessionManager.getMapper(ReceiptDocMapper.class); // основная задача данного метода ReceiptDoc receiptDoc = findReceiptDoc(id); if(!isSessionInherited) { sqlSessionManager.commit(); } return receiptDoc; } catch(Exception e) { sqlSessionManager.rollback(); } finnaly { if(!isSessionInherited) { sqlSessionManager.close(); } } }Теперь сделаем то же самое с использованием mybatis-guice
@Inject private ReceiptDocMapper mapper; @Override @Transactional(force = true) public ReceiptDoc findReceiptDoc(String id) { return mapper.findReceiptDoc(id); }
Вот как красиво и лаконично выглядит функционал взаимодействия с базой данных. Весь функционал управления сессией и транзакциями переместился в обработчик аннотации @Transactional.
Нечто подобное можно постараться реализовать на CDI JSR-299 Weld. Но меня напрягает тот факт, что ReceiptDocMapper - это интерфейс. Рисовать методы провайдера для каждого интерфейса не совсем правильно.
Осталось дело за малым - настроить нашу CDI систему. Напишем небольшой класс, помогающий нам выполнить настройки.
package org.myproject.impl.provider; import javax.sql.DataSource; import com.google.inject.Guice; import com.google.inject.Injector; public class ServicesUtil { /** * Создание JSR-330 Guice Injector. Поднятие MyBatis. * @param dataSource соединение с базой данных * @param useXA учитывать систему распределенных транзакций или нет * @param environment окружение для MyBatis * @param mappings файл настроек для MyBatis * @return */ public static Injector createInjector(DataSource dataSource, boolean useXA, String environment, String mappings) { // Нам нужен ClassLoader приватного класса в текущем OSGI Bundle. // В настройка MyBatis используются приватные классы. SessionModule module = new SessionModule(ServicesUtil.class.getClassLoader(), environment, mappings); Injector injector = Guice.createInjector(module); module.configure( injector.getInstance(Provider.class).getSqlSessionFactory(), dataSource, useXA); return injector; } }На следующий код надо запастись терпением. Он достаточно длинный.
package com.comita.esb.impl.aisgz.exchange.oos.model; import java.util.Properties; import javax.sql.DataSource; import javax.transaction.UserTransaction; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.mybatis.guice.XMLMyBatisModule; import org.mybatis.transaction.jta.JtaTransactionFactory; import com.google.inject.PrivateModule; import org.myproject.provider.Provider; public class SessionModule extends PrivateModule { private final String environmentId; private final ClassLoader classLoader; private final String classPathResource; public SessionModule( ClassLoader classLoader, String environmentId, String classPathResource) { this.environmentId = environmentId; this.classLoader = classLoader; this.classPathResource = classPathResource; } @Override protected void configure() { install(new XMLMyBatisModule(){ @Override protected void initialize() { useResourceClassLoader(classLoader); setEnvironmentId(environmentId); setClassPathResource(classPathResource); } }); bind(Provider.class).to(ProviderImpl.class); expose(Provider.class); } public void configure( SqlSessionFactory sqlSessionFactory, DataSource dataSource, boolean useXaDataSource) { Configuration configuration = sqlSessionFactory.getConfiguration(); TransactionFactory transactionFactory = configTransaction(configuration, useXaDataSource); Environment env = new Environment(environmentId, transactionFactory, dataSource); configuration.setEnvironment(env); } private TransactionFactory configTransaction(Configuration configuration, boolean useXaDataSource) { TransactionFactory transactionFactory; if(useXaDataSource) { transactionFactory = new JtaTransactionFactory(); // запрещаем данному менеджеру автоматически закрывать соединения Properties prop = new Properties(); prop.setProperty("closeConnection", "false"); prop.setProperty("UserTransaction", "osgi:service/" + UserTransaction.class.getName()); transactionFactory.setProperties(prop); } else { transactionFactory = new JdbcTransactionFactory(); } return transactionFactory; } }Обратите внимание, что в свойство "UserTransaction" я указываю название OSGi JNDI ресурс. В JavaEE 6 надо указывать другое название ресурса. Поскольку, весь проект ориентирован на OSGi, не было необходимости как-то усложнять данный функционал.
Комментариев нет:
Отправить комментарий