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

时间:2015-02-27 13:19:34

标签: hibernate spring-mvc spring-security spring-3 spring-annotations

我正在尝试仅使用一个数据源来更改注释hibernate,以保存在数据库中保存的数量。为了使用户具有其分配的连接,并添加新类型的连接,只重启服务器(避免.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());
    }

}

然后我创建了一个这样的Routing DataSource:

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;
}

使应用程序在此更改之前工作。因此,在服务器启动中访问DB似乎是一个问题。有什么想法吗?

EDIT2:

更改了第一个术语中添加的代码以发布完整的工作代码作为示例。

1 个答案:

答案 0 :(得分:1)

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

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并使数据源的数量有点动态(在DB中填入一个新条目并重新启动简单的服务器)。

考虑到将禁用延迟映射,因此评估需要设置为FetchType.Eager的内容可能很有用(如果需要使用FetchMode.Subselect进行初始化,则使用FetchMode.Subselect)< / p>

我要编辑这个问题,以便将此作为配置路由的示例&#34; dynamic&#34;带有注释的Spring 3.1.3的数据源。