Para utilização de um banco write e outro read-only em uma aplicação com Spring, siga as instruções abaixo:
1. Crie uma classe que contenha os tipos que farão distinção entre os bancos de escrita e leitura.
Por exemplo:
public enum DatabaseType { READ_ONLY, WRITE }
2. Crie uma classe que armazenará o tipo no context da thread em execução.
public class DatabaseContextHolder { private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<DatabaseType>(); public static void setDatabaseType(DatabaseType databaseType) { if (databaseType == null) databaseType = DatabaseType.WRITE; // default to write database contextHolder.set(databaseType); } public static DatabaseType getDatabaseType() { return (DatabaseType) contextHolder.get(); } public static void clearDatabaseType() { contextHolder.remove(); } }
3. Criar um DataSource específico que fará o roteamento baseado no tipo definido.
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DatabaseRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getDatabaseType(); } }
4. Criar uma annotation para definir o tipo desejado.
Vc pode criar uma onde se escolha o tipo ou uma que seja read-only e aplique onde for necessário. Vou colocar aqui o último caso:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ReadOnlyDatabase { }
5. Criar um interceptor para captar o uso da annotation.
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class DatabaseTypeAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { Annotation[] annotations = methodInvocation.getMethod().getAnnotations(); DatabaseType databaseType = DatabaseType.WRITE; for (Annotation annotation : annotations) { if (annotation instanceof Entity) databaseType = DatabaseType.READ_ONLY; } DatabaseContextHolder.setDatabaseType(databaseType); return methodInvocation.proceed(); } }
6. Feito isso é só configurar no application-context.xml.
<bean id="databaseRoutingDataSource" class="pacote.DatabaseRoutingDataSource"> <property name="targetDataSources"> <map key-type="pacote.DatabaseType"> <entry key="WRITE" value-ref="writeDataSource"/> <entry key="READ_ONLY" value-ref="readOnlyDataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="writeDataSource"/> </bean>
Note que no "sessionFactory" vc deve definir o datasource acima.
Defina o interceptor:
<bean id="databaseTypeAdvice" class="pacote.DatabaseTypeAdvice"/>
E no "applicationTxProxyTemplate" inclua a seguinte propriedade que referencie o interceptor:
<property name="preInterceptors"> <list> <ref local="dataSourceTypeAdvice"/> </list> </property>