Change SessionFactory datasource jdbcurl late in runtime - spring

I'm writing a desktop java application for an environment without a network connection. I'm trying to store the application data as securely as I can in an encrypted in-process hsqldb, with an unencrypted user information hsqldb. Hsqldb requires that the crypto_key be set in the jdbcurl when the connection is created. My application uses hibernate to do persistence and Spring to do configuration and injection.
My current scheme is to store username, password hash, salt and the encrypted database's crypto_key in the unencrypted user table. The crypto_key is protected by an asymmetric encryption using the user's password as the key. Thus, the application doesn't know what the crypto_key for the application data is until after it has been running long enough to load a gui, and authenticate the user.
Here is my current applicationContext.xml. Spring uses it to get Hibernate going and functioning.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:component-scan base-package="com.company.domain" />
<context:component-scan base-package="com.company.service" />
<tx:annotation-driven />
<bean id="userDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url"
value="jdbc:hsqldb:./ReviewDatabase/users" />
<property name="username" value="reviewer" />
<property name="password" value="$kelatonKey" />
</bean>
<bean id="mainDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url"
value="jdbc:hsqldb:./ReviewDatabase/data" /> <!-- TODO: ;crypt_key=;crypt_type=AES -->
<property name="username" value="reviewer" />
<property name="password" value="$kelatonKey" />
</bean>
<bean id="userSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="userDataSource" />
<property name="annotatedClasses">
<list>
<value>com.company.domain.AppUser</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="mainSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="mainDataSource" />
<property name="annotatedClasses">
<list>
<!-- <value>com.companu.domain.Person</value> -->
<!-- <value>com.company.domain.Thing</value> -->
<!-- <value>com.company.domain.Thing1</value> -->
<!-- <value>com.company.domain.Thing2</value> -->
<!-- <value>com.company.domain.Review</value> -->
</list>
</property>
<property name="hibernateProperties">
<props>
<pro key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="mainTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="mainSessionFactory" />
</bean>
<bean id="userTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="userSessionFactory" />
</bean>
</beans>
Here's an example of a class where I would like to have the SessionFactory injected
#Repository("ReviewDao")
public class HibernateReviewDao implements ReviewDao{
private SessionFactory mainSessionFactory;
#Autowired
public void setMainSessionFactory(
SessionFactory mainSessionFactory){
this.mainSessionFactory = mainSessionFactory;
}
#Override
#Transactional(value = "mainTransactionManager")
public void store(Review review) {
mainSessionFactory.getCurrentSession().saveOrUpdate(review);
}
#Override
#Transactional(value = "mainTransactionManager")
public void delete(Long reviewId) {
Review review = (Review)mainSessionFactory.getCurrentSession()
.get(Review.class, reviewId);
mainSessionFactory.getCurrentSession().delete(review);
}
}
And finally, here's what I've tried to do after authenticating the user and getting that crypto_key.
String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data2;crypt_key=" + secret + ";crypt_type=AES";
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySetting("hibernate.dialect", "org.hibernate.dialect.HSQLDialect")
.applySetting("hibernate.show_sql", "true")
.applySetting("hibernate.hbm2ddl.auto","update")
.applySetting("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver")
.applySetting("hibernate.connection.url", jdbcUrl)
.applySetting("hibernate.connection.username", "reviewer")
.applySetting("hibernate.connection.password", "$kelatonKey")
.buildServiceRegistry();
SessionFactory mainSessionFactory = new MetadataSources(serviceRegistry)
.addAnnotatedClass(com.company.domain.Review.class)
.addAnnotatedClass(com.company.domain.Person.class)
.addAnnotatedClass(com.company.domain.Thing.class)
.addAnnotatedClass(com.company.domain.Thing1.class)
.addAnnotatedClass(com.company.domain.Thing2.class)
.buildMetadata()
.buildSessionFactory();
org.springframework.orm.hibernate4.HibernateTransactionManager htm =
(HibernateTransactionManager)context.getBean("mainTransactionManager");
context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory");
htm.setSessionFactory(mainSessionFactory);
However, with that, the first query to the object above results in org.hibernate.HibernateException: No Session found for current thread
How can I change the jdbcurl long after hibernate has initialized, dependencies have been injected and other various kinds of tom-foolery has occurred?
I've been putting this part of development off, hoping Google would eventually come through, but I'm out of ideas to search for. All answers will be accepted with sheepish humility :)

I wonder if this might help, Can I replace a Spring bean definition at runtime? , you could dummy up the bean properties to start with and then change the bean in runtime.

So, the missing bit of the recipe was LocalSessionFactoryBean. It got the sessionFactory setup so I could just replace the sessionFactories that are created at initialization.
Here's the code I had to change from the question
org.springframework.orm.hibernate4.HibernateTransactionManager htm =
(HibernateTransactionManager)context.getBean("mainTransactionManager");
Class<?>[] classes = new Class<?>[5];
classes[0] = com.company.domain.Thing1.class;
classes[1] = com.company.domain.Thing2.class;
classes[2] = com.company.domain.Person.class;
classes[3] = com.company.domain.Thing.class;
classes[4] = com.company.domain.Review.class;
String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data3;crypt_key=" + secret + ";crypt_type=AES";
java.util.Properties hibernateProperties = new java.util.Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto","update");
hibernateProperties.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
hibernateProperties.setProperty("hibernate.connection.url", jdbcUrl);
hibernateProperties.setProperty("hibernate.connection.username", "reviewer");
hibernateProperties.setProperty("hibernate.connection.password", "$kelatonKey");
LocalSessionFactoryBean slfb = new LocalSessionFactoryBean();
slfb.setHibernateProperties(hibernateProperties);
slfb.setAnnotatedClasses(classes);
try {
slfb.afterPropertiesSet();
} catch (IOException e) {
Log.warn("Cannot connection to application database");
Log.write(e.getLocalizedMessage());
Log.write(e.getStackTrace());
return;
}
SessionFactory mainSessionFactory = slfb.getObject();
context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory");
htm.setSessionFactory(mainSessionFactory);
for(ListenForNewSessionFactory dao : daos){
dao.setNewSessionFactory(mainSessionFactory);
}
I had each Dao implement an interface to set the sessionFactory, and had each of them add themselves to a static list on initialization. It's not very reusable, but it works.

I used the following hack - wherever I needed a SessionFactory, I used a SessionFactoryFactory (below) instead - delegates the only SessionFactory method I actually use.
#Component
public class SessionFactoryFactory {
#Autowired
private LocalSessionFactoryBean sessionFactoryBean;
#Autowired
private DriverManagerDataSource dataSource;
private SessionFactory sessionFactory;
private SessionFactory getSessionFactory() {
if (null == sessionFactory) {
sessionFactory = sessionFactoryBean.getObject();
}
return sessionFactory;
}
public Session openSession() {
return getSessionFactory().openSession();
}
public void updateDataSourceUrl() throws IOException {
sessionFactory = null;
sessionFactoryBean.afterPropertiesSet();
}
}

Related

Spring Data JPA + Hibernate : Inject Catalog/Schema at runtime in to JPA Entities

We have a scenario where in the catalog/schema combination is different for the entity classes inside certain package from the default one used by all others. I am trying to set Catalog and Schema on #Table annotation using PersistenceUnitPostProcessors callback at runtime using javaassist as below.
The issue: The added member values on javaassist annotation are NOT getting reflected on to the actual class associated with it. Please help me in finding the wrong lines of code; OR if there are other ways to achieve this, more than happy to know.
Note: I do not want to create a separate EntityManagerFactory for each catalog/schema combination - it is not really required in our case as the datasource is same.
related content in spring context :
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
<bean name="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="mainUnit" />
<property name="packagesToScan" value="com.mycompany.lob.domain" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitPostProcessors">
<list>
<bean class="com.mycompany.lob.jpa.CustomPersistenceUnitPostProcessor"/>
</list>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SqlmxDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">100</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.default_schema">DEFAULT_SCHEMA</prop>
<prop key="hibernate.default_catalog">DEFAULT_CATALOG</prop>
</props>
</property>
</bean>
PersistenceUnitPostProcessors callback :
public class CustomPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
#Value("${user.schema}")
private String userSchema;
#Value("${user.catalog}")
private String userCatalog;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomPersistenceUnitPostProcessor.class);
#SuppressWarnings("unchecked")
#Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
LOGGER.info("MutablePersistenceUnitInfo : {} ",pui);
List<String> jpadomains = pui.getManagedClassNames();
for (Iterator<?> iterator = jpadomains.iterator(); iterator.hasNext();) {
String clazzName = (String) iterator.next();
if(clazzName.startsWith("com.mycompany.lob.domain.user")){
try {
//modify annotation attributes using JavaAssist
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(clazzName);
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute)classFile.getAttribute(AnnotationsAttribute.visibleTag);
if(annotationsAttribute!=null){
//Get hold of #Table annotation
Annotation tableAnnotation = annotationsAttribute.getAnnotation("javax.persistence.Table");
if(tableAnnotation!=null){
tableAnnotation.addMemberValue("catalog", new StringMemberValue(userCatalog, constPool));
tableAnnotation.addMemberValue("schema", new StringMemberValue(userSchema, constPool));
annotationsAttribute.addAnnotation(tableAnnotation);
LOGGER.debug("Schema-Table : {} - {} ", ((StringMemberValue)tableAnnotation.getMemberValue("schema")).getValue(),
((StringMemberValue)tableAnnotation.getMemberValue("name")).getValue() );
//write the file back
ctClass.writeFile();
}
}
} catch (Exception e) {
LOGGER.error("Schema/Catalog could not be altered for {} ",clazzName);
}
}
}
}
}
Simple answer:
19. Multitenancy
Complex catalog mapping:
interface PhysicalNamingStrategy in Hibernate v5 is helpful.
public interface PhysicalNamingStrategy {
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment);
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment);
....
}
Check the Example 2. Example PhysicalNamingStrategy implementation in Hibernate 5 User Guide and how to config it

Spring + #Transactional: can't rollback in case of error

I am trying to implement the following: I need to add two different entities in same same transaction to database.
I have different DAO classes and Service classes for each entity.
public class InvoicesDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Invoice object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class RequestsDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Request object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class InvoicesService {
#Autowired
private InvoicesDAO invoicesDAO;
#Autowired
private RequestsDAO requestsDAO;
#Transactional
public void add(Invoice object) throws HibernateException {
invoicesDAO.save(object);
}
#Transactional
public void updateAndGenerate(Invoice object1, Request object2) throws HibernateException {
invoicesDAO.save(object1);
requestsDAO.save(object2);
}
}
The config:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/hibernate.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.ejl.butler.object.data" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:annotation-config />
<context:component-scan base-package="com.service" />
<bean id="invoicesDao" class="com.dao.InvoicesDAO" />
<bean id="requestsDao" class="com.dao.RequestsDAO" />
Controller:
//***
/**
* Invoices access service
*/
#Autowired
private InvoicesService invoicesService;
// objects creation
invoicesService.updateAndGenerate(invoice, request);
//***
So when I am trying to call updateAndGenerate method and pass there invalid values for object2 - it fails without rolling back the object1. How can I fix it? Thank you
I dont think it is got to do with Proxies. You dont need a proxy object here. Generally you need a proxy object for instances such for a login service etc where you need a proxy object for the singleton bean definition. But, the only way it can not rollback is if your propogation level on the Transaction isnt correct.
If you use a Trasaction.REQUIRES_NEW then the dao.save wouldnt rollback and it wouldnt tie back to the outer transaction and hence wouldnt rollback.
Finally I figured out where the problem was so I will answer my own question...
According to Declarative transactions (#Transactional) doesn't work with #Repository in Spring and https://stackoverflow.com/a/3250959/705869 the order of the base-package items inside context:component-scan directive is very important. In additional, you should put only really necessary packages.
I had some duplicates inside this directive so the application context was initialized before database context. And that's why transactions were disabled inside services!
So check twice for base-package packages inside context:component-scan and remove unnecessary ones.

DbUnit H2 in memory db with Spring and Hibernate

Hi I'm trying a little POC with JPA and unit test to verify that the DB schema is created. I'm working with H2 DB and I set to Hibernate create the schema from the entities, but when DbUnit tries to initialize the DB from a dataset I always get a Table ... not found in tableMap. I read that I have to add the property DB_CLOSE_DELAY=-1 to DB URL but is like after Hibernate creates the schema the DB is losted when DbUnit tries to initialize.
Any ideas? Any help is highly appreciated.
This is my config:
application-context.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceH2" />
<property name="packagesToScan" value="com.xxx.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<!-- property name="databasePlatform" value="org.hibernate.dialect.MySQLInnoDBDialect" /-->
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<!-- property name="database" value="MYSQL" /-->
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="javax.persistence.validation.mode">CALLBACK</prop>
</props>
</property>
</bean>
<bean id="dataSourceH2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
RepositoryTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/application-context-test.xml" })
#Transactional
public class SystemEntityRepositoryH2Test {
#Inject
private SystemEntityRepository repository;
#Inject
private DataSource dataSourceH2;
private IDatabaseConnection connection;
#Before
public void setUp() throws Exception {
IDatabaseConnection dbUnitCon = null;
dbUnitCon = new DatabaseDataSourceConnection(dataSourceH2, "testdb");
dbUnitCon.getConfig().setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
IDataSet dataSet = this.getDataSet("dataset-systementity.xml");
DatabaseOperation.INSERT.execute(dbUnitCon, dataSet);
}
#After
public void tearDown() throws Exception {
//DatabaseOperation.DELETE_ALL.execute(this.getConnection(), this.getDataSet(dataSetFile));
}
#Test
public void test() throws Exception {
}
protected IDataSet getDataSet(String dataSetFile) throws Exception {
ResourceLoader resourceLoader = new ClassRelativeResourceLoader(this.getClass());
Resource resource = resourceLoader.getResource(dataSetFile);
if (resource.exists()) {
return new FlatXmlDataSetBuilder().build(resource.getInputStream());
}
return null;
}
}
dataset-systementity.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<System_Entities id="2" name="NAME" phone01="+52-55-55555555" email="a#a.com"
address01="Street" address02="123" address03="1" address04="Address04"
address05="Address05" city="City" state="State" country="MX"
zipcode="12345" />
</dataset>
Error
ERROR DatabaseDataSet:286 - Table 'System_Entities' not found in tableMap=org.dbunit.dataset.OrderedTableNameMap[_tableNames=[], _tableMap={}, _caseSensitiveTableNames=false]
I can see that the tables are created by hibernate because the log shows all the sql sentences without error.
Thanks.
SOLUTION
Thanks Mark Robinson
I modified the setUp method to:
#Before
public void setUp() throws Exception {
IDatabaseConnection dbUnitCon = null;
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
SessionImplementor si = (SessionImplementor) session;
Connection conn = si.getJdbcConnectionAccess().obtainConnection();
dbUnitCon = new DatabaseConnection(conn);
//dbUnitCon.getConfig().setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
IDataSet dataSet = this.getDataSet("dataset-systementity.xml");
DatabaseOperation.INSERT.execute(dbUnitCon, dataSet);
}
It works now, what I don't understand yet is if I use HSQLDB I don't have this problem.
The problem is that DBUnit is loading the table data before Hibernate can initialize.
As part of your #setup, you'll need to get the Hibernate session. This should cause Hibernate to create your table. You could even force it by executing a simple query like select 1

Trying to persist to an oracle db using hibernate 4 and spring 3 without success. Tests pass but data not persisted.

I am trying to implement a simple DAO using hibernate 4 and Spring 3.
When I try to save or delete a row in the db the transaction is not persisted. I have included some code to show how the saving in the db doesnt work:
I have a junit test which simply tries to save a StockEntityDTO in the db.
#RunWith(SpringJUnit4ClassRunner.class)
public class StocksDAOImplTest extends
AbstractTransactionalJUnit4SpringContextTests {
#Autowired
protected StocksDAO stockDao;
#Test
public void shouldInsertIntoDatabase() {
BigDecimal price = new BigDecimal(653.50);
StockEntityDTO savedStock = new StockEntityDTO("GOOG", price, "google");
stockDao.create(savedStock);
StockEntityDTO retrievedStock = stockDao.getById(savedStock.getId());
assertEquals(savedStock, retrievedStock);
}
The test passes but the expected row (1, "GOOG", 653.50, "google") is not persisted in the db.
The DAO looks like this:
#Transactional
public abstract class AbstractHibernateDAO<T extends Serializable> {
private Class<T> clazz;
#Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
public void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public void create(final T entity) {
Session session = this.getCurrentSession();
session.save(entity);
}
Application Context:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:orcl" />
<property name="username" value="gtp" />
<property name="password" value="gtp" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.ubs.gtp.data.domain" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
</prop>
</props>
</property>
</bean>
Hope someone can help. As is probably evident from my code, I am very new to spring.
AbstractTransactionalJUnit4SpringContextTests rolls back after the test. Try setting a breakpoint at the last line and then inspecting the database. You can use the Rollback annotation if you don't want this default behaviour.

Injection of the sessionFactory not working

I'm new with Spring and I want to inject the sessionFactory and its not working at all. I'm starting a web server Jetty and then I load the context. After i start a GWT web application, I make a call on the server side and I try to get some info but I get a null pointer. There is no error at all so it make it difficult to know where the problem is. I know its suppose to work because I saw it working on a project I worked on some times ago. Any help will be appreciate. (Sorry for my possibly bad english)
Here is the context.xml :
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema
/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema
/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema
/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema
/security/spring-security-3.0.xsd">
<!-- Standard spring initialization -->
<context:component-scan base-package="com.test">
</context:component-scan>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- Connection to the database-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-
method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123456789" />
</bean>
<!-- Hibernate session factory-->
<bean id="jpaSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.domain"/>
<property name="namingStrategy" >
<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- Hibernate session factory -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="jpaSessionFactory" />
</bean>
</beans>
Here is the main :
public static void main(String[] args) {
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[]
{ new AppContextBuilder().buildWebAppContext()});
final JettyServer jettyServer = new JettyServer();
jettyServer.setHandler(contexts);
Runnable runner = new Runnable() {
#Override
public void run() {
new ServerRunner(jettyServer);
}
};
EventQueue.invokeLater(runner);
new ClassPathXmlApplicationContext("context.xml");
}
Here is the class Test where I want the injection :
#Component("test")
public class TEST{
#Resource(name="jpaSessionFactory")
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
#Transactional(readOnly = true)
public List<Personne> getPersonnes() {
Session s = sessionFactory.getCurrentSession();
Query query = s.createQuery("from Person");
return query.list();
}
}
Here is the server side from the application (not shown completely)
/*** The server side implementation of the RPC service.
*/
#SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
#Resource
private TEST t;
public String greetServer(String input) throws IllegalArgumentException {
// Verify that the input is valid.
if (!FieldVerifier.isValidName(input)) {
// If the input is not valid, throw an IllegalArgumentException
back to
// the client.
throw new IllegalArgumentException(
"Name must be at least 4 characters long");
}
t.getPersons(); // NULL pointer here
.........................
The MySQL tables are created like it should, so all the scanning seems to work.
Thanks
Bob
This is because the GreetingServiceImpl class is not created by spring - it is servlet that is initialized by the container directly. Follow the instructions in this article to fix the issue. I have copied the relevant code from the article.
#Override
public void init() throws ServletException {
super.init();
final WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
if (ctx == null) {
throw new IllegalStateException("No Spring web application context found");
}
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}

Resources