设置
我有一个使用Spring 4.3,JdbcTemplate,Hibernate 5和MySQL 8的应用程序。我已经在每个模式的hibernate中实现了多租户,其中我使用hibernates多租户机制-MultiTenantConnectionProvider
来切换模式,并且基本上在那做:
connection.createStatement().execute("USE " + databaseNamePrefix + tenantIdentifier);
,这有效。
现在,我的应用程序的报告部分使用JdbcTemplate
来查询数据库。
现在,我想类似地在JdbcTemplate执行的每个查询之前发出此USE tenantIdentifier
语句。
问题
如何为JdbcTemplate执行的每个查询添加一些SQL或语句?
我尝试过的事情
我调查了JdbcTemplate,发现的只有设置NativeJdbcExtractor
。我已经尝试了下面的代码,但是他正在通过这种方法登录甚至不是loggin。
@Bean
@DependsOn("dataSource")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
jdbcTemplate.setNativeJdbcExtractor(new SimpleNativeJdbcExtractor(){
@Override
public Connection getNativeConnection(Connection con) throws SQLException {
LOGGER.info("getNativeConnection");
System.out.println("aaa");
return super.getNativeConnection(con);
}
@Override
public Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException {
System.out.println("aaa");
LOGGER.info("getNativeConnectionFromStatement");
return super.getNativeConnectionFromStatement(stmt);
}
});
return jdbcTemplate;
}
向Spring添加了功能请求:https://jira.spring.io/browse/SPR-17342
编辑:我看着Spring 5,他们删除了JdbcExtractor,所以这绝对是错误的路径。
答案 0 :(得分:1)
用JdbcTemplate
做到这一点将不是一个简单的方法,因为某些方法非常通用,例如execute(ConnectionCallback<T> action)
方法允许直接访问java.sql.Connection
对象。
为每个租户拥有一个单独的DataSource
bean并在Spring中使用合格的自动装配来解决这个问题会更容易。
在打开新的数据库连接时,具有按租户的java.sql.Connection
将允许执行USE tenantIdentifier
语句(某些池库支持此功能)。由于MySQL USE statement docs可以在每个会话中完成一次:
USE db_name语句告诉MySQL将db_name数据库用作后续语句的默认(当前)数据库。数据库将保持默认状态,直到会话结束或发出另一个USE语句为止。
答案 1 :(得分:0)
不创建jdbc模板bean。相反,每次需要执行查询时,都可以使用实体管理器工厂来创建jdbc模板的新实例。 这种方法对我有用。
public class JdbcQueryTemplate {
public JdbcTemplate getJdbcTemplate(EntityManagerFactory emf) {
EntityManagerFactoryInfo info = (EntityManagerFactoryInfo) emf;
return new JdbcTemplate(info.getDataSource());
}
public NamedParameterJdbcTemplate getNamedJdbcTemplate(EntityManagerFactory emf) {
EntityManagerFactoryInfo info = (EntityManagerFactoryInfo) emf;
return new NamedParameterJdbcTemplate(info.getDataSource());
}
}
然后使用该类进行查询。
public class Test{
@Autowired
private EntityManagerFactory entityManagerFactory;
public List<Entity> executeQuery() {
return new JdbcQueryTemplate().getNamedJdbcTemplate(entityManagerFactory)
.query("query", new BeanPropertyRowMapper<>(Entity.class));
}
}
答案 2 :(得分:0)
已经晚了,但这是我的解决方案,也许可以帮助某人。不是很优雅,但是效果很好。
对于春季5,我覆盖了JdbcTemplate
:
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.sql.*;
public class TenantJdbcTemplate extends JdbcTemplate {
public TenantJdbcTemplate(DataSource dataSource) {
super(dataSource);
}
@Override
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
boolean isTransactionalCon = isTransactionalConnection(con, getDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (!isTransactionalCon) {
setSchema(stmtToUse);
}
T result = action.doInStatement(stmtToUse);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
@Override
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}
Connection con = DataSourceUtils.getConnection(obtainDataSource());
boolean isTransactionalCon = isTransactionalConnection(con, getDataSource());
PreparedStatement ps = null;
try {
ps = psc.createPreparedStatement(con);
applyStatementSettings(ps);
if (!isTransactionalCon) {
try (Statement stmt = con.createStatement()) {
setSchema(stmt);
}
}
T result = action.doInPreparedStatement(ps);
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
@Override
public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)
throws DataAccessException {
Assert.notNull(csc, "CallableStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(csc);
logger.debug("Calling stored procedure" + (sql != null ? " [" + sql + "]" : ""));
}
Connection con = DataSourceUtils.getConnection(obtainDataSource());
boolean isTransactionalCon = isTransactionalConnection(con, getDataSource());
CallableStatement cs = null;
try {
cs = csc.createCallableStatement(con);
applyStatementSettings(cs);
if (!isTransactionalCon) {
try (Statement stmt = con.createStatement()) {
setSchema(stmt);
}
}
T result = action.doInCallableStatement(cs);
handleWarnings(cs);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (csc instanceof ParameterDisposer) {
((ParameterDisposer) csc).cleanupParameters();
}
String sql = getSql(csc);
csc = null;
JdbcUtils.closeStatement(cs);
cs = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("CallableStatementCallback", sql, ex);
}
finally {
if (csc instanceof ParameterDisposer) {
((ParameterDisposer) csc).cleanupParameters();
}
JdbcUtils.closeStatement(cs);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
private static void setSchema(Statement stmt) throws SQLException {
stmt.execute("set search_path=\"" + TenantIdHolder.getTenantId() + "\";");
}
private static String getSql(Object sqlProvider) {
if (sqlProvider instanceof SqlProvider) {
return ((SqlProvider) sqlProvider).getSql();
}
else {
return null;
}
}
private static boolean isTransactionalConnection(Connection connection, DataSource dataSource) {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
return conHolder != null && conHolder.getConnection() == connection;
}
}