带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置

Posted

技术标签:

【中文标题】带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置【英文标题】:Spring 3.1.3 + Hibernate Configuration with annotations and with (Dynamic) AbstractRoutingDataSource 【发布时间】:2015-04-30 05:26:14 【问题描述】:

我正在尝试仅使用一个数据源更改注释休眠状态,以使尽可能多的数据源保存在数据库中。为了使用户拥有分配的连接,并添加新类型的连接,只需重新启动服务器(避免 .war 重新编译)

服务器首先加载一个 SecurityHibernateConfiguration 没有任何问题:

@Configuration
@EnableTransactionManagement
public class SecurityHibernateConfiguration 

    @Autowired
    public Parameters parameters;

    @Bean
    DataSource datasourcesecurity() 

        org.apache.commons.dbcp.BasicDataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();

        dataSource.setDriverClassName(parameters.getDriverClassName());
        dataSource.setUrl(parameters.getUrlSecurity());
        dataSource.setUsername(parameters.getUserNameSecurity());
        dataSource.setPassword(parameters.getPasswordSecurity());

        return dataSource;
    

    @Bean
    public SessionFactory securitySessionFactory() throws Exception 
        Properties props = new Properties();
        props.put("hibernate.dialect", parameters.getHibernateDialect());
        props.put("hibernate.format_sql", parameters.getFormatSql());

        AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean();
        bean.setAnnotatedClasses(new Class[] 
                    Login.class,       
                    LoginRol.class,
                    Aplicacio.class,
                    Rol.class,
                    RolObjecte.class,
                    Objecte.class,
                    RolObjecteAcl.class,
                    Acl.class,
                    Tema.class,
                    Connexio.class
        );
        bean.setHibernateProperties(props);
        bean.setDataSource(datasourcesecurity());
        bean.setSchemaUpdate(false);
        bean.afterPropertiesSet();

        SessionFactory factory = bean.getObject();
        return factory;
    

    @Bean
    public HibernateTransactionManager securitytransactionManager() throws Exception 
        return new HibernateTransactionManager(securitySessionFactory());
    


然后我创建了一个这样的路由数据源:

public class RoutingDataSource extends AbstractRoutingDataSource 
    @Autowired
    private SecurityManager securitymanager;

    private Map<Long, DataSource> targetDataSources = new HashMap<Long, DataSource>();

    @SuppressWarnings( "unchecked", "rawtypes" )
    public void setTargetDataSources(Map targetDataSources) 
        this.targetDataSources = (Map<Long, DataSource>) targetDataSources;
    

    @Override
    protected DataSource determineTargetDataSource() 
        Long lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.targetDataSources.get(lookupKey);
        if (dataSource == null) 
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        
        return dataSource;
    

    @Override
    protected Long determineCurrentLookupKey() 
        try 
            String username = securitymanager.getUserName();
            Login login = null;
            if (!StringUtils.isEmpty(username)) 
                login = securitymanager.getLogin(username);
            
            return login == null ? 1L : login.getConnexio() == null ? 1L : login.getConnexio().getId();
         catch (Exception e) 
            return 1L;
        
    

    @Override
    public void afterPropertiesSet() 
        // do nothing
        // overridden to avoid datasource validation error by Spring
    


在 HibernateConfiguration 中这样使用:

@Configuration
@EnableTransactionManagement
public class HibernateConfiguration 
    @Autowired
    private SecurityManager securitymanager;
    @Autowired
    private Parameters parameters;

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() 

        LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();

        lcemfb.setDataSource(this.dataSource());
        lcemfb.setPackagesToScan(new String[]  "cat.itec.pgm.persistence" );
        lcemfb.setPersistenceUnitName("pgmdb");

        HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
        va.setShowSql(true);
        lcemfb.setJpaVendorAdapter(va);

        Properties ps = new Properties();
        ps.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
        ps.put("hibernate.format_sql", "true");
        ps.put("hibernate.show_sql", "true");
        lcemfb.setJpaProperties(ps);

        lcemfb.afterPropertiesSet();
        return lcemfb;
    

    @Bean
    public DataSource dataSource() 

        RoutingDataSource rds = new RoutingDataSource();
        Map<Long, DataSource> targetDataSources = new HashMap<Long, DataSource>();
        List<Connexio> connexioLogins = new ArrayList<Connexio>();
        try 
            connexioLogins = securitymanager.getConnexioLogins();
         catch (Exception e) 
            System.out.println("Cannot Load List Of Connections");
        
        for (Connexio c : connexioLogins) 
            DriverManagerDataSource ds = new DriverManagerDataSource();
            ds.setDriverClassName(parameters.getDriverClassName());
            ds.setUrl(generateUrlConnection(c));
            ds.setUsername(c.getDbUsername());
            ds.setPassword(c.getDbPassword());
            targetDataSources.put(c.getId(), ds);
        
        rds.setTargetDataSources(targetDataSources);
        return rds;
    

    @Bean
    public PlatformTransactionManager transactionManager() 
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(this.entityManagerFactoryBean().getObject());
        return tm;
    

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() 
        return new PersistenceExceptionTranslationPostProcessor();
    

    private String generateUrlConnection(Connexio c) 
        StringBuilder sb = new StringBuilder();
        sb.append("jdbc:oracle:thin:@");
        sb.append(c.getServer());
        sb.append(":");
        sb.append(c.getPort());
        sb.append(":");
        sb.append(c.getSid());
        return sb.toString();
    

关键是当我启动服务器时它会抛出一个:

Caused by: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:685)
    at cat.itec.security.persistence.dao.login.impl.LoginDaoImpl.getConnexioLogins(LoginDaoImpl.java:37)

不知道是怎么报错让RoutingDataSource得到每一个“Connexio”,还是没有正确配置。

任何帮助或评论将不胜感激。 (我会尽快发布任何其他需要更好理解的代码)。

提前致谢。

编辑(无用,参见 EDIT2):

像这样稍微改变两个冲突的数据库点:

@Bean
public DataSource dataSource() 
    RoutingDataSource rds = new RoutingDataSource();
    Map<Long,DataSource> targetDataSources = new HashMap<Long,DataSource>();
    Connexio c = new Connexio();
    c.setDbPassword("XXXXXXXXX");
    c.setDbUsername("XXX");
    c.setId(1L);
    c.setPort("XXXXXXX");
    c.setServer("XXXXXXXX");
    c.setSid("XXXX");
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName(parameters.getDriverClassName());
    ds.setUrl(generateUrlConnection(c));
    ds.setUsername(c.getDbUsername());
    ds.setPassword(c.getDbPassword());
    targetDataSources.put(c.getId(), ds);
    rds.setTargetDataSources(targetDataSources);

    return rds;

@Override
protected Long determineCurrentLookupKey() 
    return 1L;

使应用程序像此更改之前一样工作。所以在服务器启动时访问数据库似乎是一个问题。有什么想法吗?

EDIT2:

更改了第一学期添加的代码,以发布完整的工作代码作为示例。

【问题讨论】:

【参考方案1】:

我发现问题出在我的道层。在服务器启动时无法访问当前会话,所以我做了类似的事情:

try 
    Session session = securitySessionFactory.getCurrentSession();
    Criteria crit = session.createCriteria(clazz);
    return (List<T>) crit.list();
 catch (Exception e) 
    Session session = securitySessionFactory.openSession();
    Transaction transaction = session.beginTransaction();
    transaction.begin();
    Criteria crit = session.createCriteria(clazz);
    List<T> list = (List<T>) crit.list();
    session.disconnect();
    session.close();
    return list;

这样我可以正确填充 RoutingDataSources 并使数据源的数量有点动态(在数据库中填充一个新条目并简单地重新启动服务器)。

考虑到惰性映射将被禁用,因此评估需要设置为 FetchType.Eager 的内容可能很有用(如果需要用它初始化 1 个以上的包,则使用 FetchMode.Subselect)

我将编辑该问题,以便将此作为示例,为 Spring 3.1.3 配置路由“动态”数据源并使用注释。

【讨论】:

以上是关于带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置的主要内容,如果未能解决你的问题,请参考以下文章

如何在 @Table 注释中动态设置模式名称

SVG 中的动态注释

使用带有注释更改的 matplotlib 交互式条形图

披露按钮地图视图

未找到改造注释。 (参数#1)

数据注释/验证和动态值