Entity:
package com.test.entity
#Entity
#Table(name="TEST_TABLE")
public class TestTable implements HasMapping, PrimaryKey<Long>, Serializable {
public static final String TABLE_NAME = "TEST_TABLE";
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID", nullable=false)
private Long id;
#Id
#Column(name="Name", nullable=false)
private String name;
#Temporal(value=TemporalType.TIMESTAMP)
#Column(name="CREATE_TIME", nullable=true)
#CreatedDate
private Date createTime;
#Column(name="CREATE_USER", nullable=true, length=32)
#CreatedBy
private String createUser;
#Temporal(value=TemporalType.TIMESTAMP)
#Column(name="LST_UPD_TIME", nullable=true)
#LastModifiedDate
private Date lstUpdTime;
#Column(name="LST_UPD_USER", nullable=true, length=32)
#LastModifiedBy
private String lstUpdUser;
#Column(name="JPA_VERSION", nullable=false)
#Version
private Integer jpaVersion;
......
}
QPath
package com.test.qpath
#Generated("com.mysema.query.codegen.EntitySerializer")
public class QTestTable extends EntityPathBase<TestTable> {
private static final long serialVersionUID = -1751805455;
public static final QTestTable testTable = new QTestTable("testTable");
public final NumberPath<Long> id = createNumber("id", Long.class);
public final DateTimePath<java.util.Date> createTime = createDateTime("createTime", java.util.Date.class);
public final StringPath createUser = createString("createUser");
public final NumberPath<Integer> jpaVersion = createNumber("jpaVersion", Integer.class);
public final DateTimePath<java.util.Date> lstUpdTime = createDateTime("lstUpdTime", java.util.Date.class);
public final StringPath lstUpdUser = createString("lstUpdUser");
public QTestTable(String variable) {
super(TestTable.class, forVariable(variable));
}
#SuppressWarnings("all")
public QTestTable(Path<? extends TestTable> path) {
super((Class)path.getType(), path.getMetadata());
}
public QTestTable(PathMetadata<?> metadata) {
super(TestTable.class, metadata);
}
......
}
Repository
package com.test.repos
public interface RTestTable extends JpaRepository<TestTable, Long>, QueryDslPredicateExecutor<TestTable> {
}
Service
package com.test.service
#Service
public class TestServiceR() {
#Autowired
private RTestTable rTestTable;
public void handler() {
long id = 1;
TestTable t = rTestTable.findone(id);
}
}
package com.test.service
#Service
public class TestServiceQuery() {
#Autowired
private RTestTable rTestTable;
#PersistenceContext
private EntityManager em;
private QTestTable qTestTable = QTestTable.testTable;
public void handler() {
long id = 1;
JPAQuery query = new JPAQuery(em);
TestTable t = query.from(qTestTable).where(qTestTable.id.eq(id)).singleResult(qTestTable);
}
}
spring config
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="#{env.jdbcUrl}" />
<property name="username" value="#{env.jdbcUsername}" />
<property name="password" value="#{env.jdbcPassword}" />
<property name="initialSize" value="1" />
<property name="minIdle" value="#{env['jdbcMinIdle'] ?: 2 }" />
<property name="maxActive" value="#{env['jdbcMaxActive'] ?: 20}" />
<property name="minEvictableIdleTimeMillis" value="#{env['jdbcMinEvictableIdleTimeMillis'] ?: 1800000}" />
<property name="validationQuery" value="#{env['jdbcTestSql']}" />
<property name="testWhileIdle" value="#{env['jdbcTestWhileIdle']?: false}" />
<property name="testOnBorrow" value="#{env['jdbcTestOnBorrow']?: true}" />
<property name="testOnReturn" value="#{env['jdbcTestOnReturn']?: false}" />
<property name="poolPreparedStatements" value="false" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="-1" />
<property name="filters" value="mergeStat,slf4j" />
<property name="connectionProperties" value="druid.stat.slowSqlMillis=1500;druid.stat.logSlowSql=true" />
<property name="timeBetweenLogStatsMillis" value="900000" />
</bean>
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="default" />
<property name="packagesToScan">
<list>
<value>com.sunline.ccs.infrastructure.shared.model</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="#{env['jpaDatabaseType']?:'DEFAULT'}" />
<property name="showSql" value="#{env['jpaShowSql']?:false}" />
</bean>
</property>
</bean>
<bean id="sessionFactory" factory-bean="emf" factory-method="getSessionFactory" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
<jpa:repositories base-package="com.test.repos" />
I test TestServiceR.handler() and TestServiceQuery.handler().
I think they don't start a transaction.
but class TestServiceR are start a transaction.
why? how can i set TestServiceR.handler() don't start a transaction.
TestServiceR calls RTestTable.findOne which extends JpaRepository, which is implemented by SimpleJpaRepository, which is annotated with #Transactional(readOnly = true). So, the transaction is started by SimpleJpaRepository.
Is there a particular reason why you are worried about the transaction, given that it does not affect the application adversely (at least not much)? See the comment history for this Spring Data JPA JIRA issue if you are not convinced.
If you still want to override the default behaviour, you can initialise JPA repositories as #EnableJpaRepositories(enableDefaultTransactions = false) (Java configuration) or <jpa:repositories enable-default-transactions="false" ... /> (XML configuration) to prevent the default implementation from creating transactions by default. See this Spring Data JPA JIRA issue for more details.
Related
I am trying to upgrade Spring from 4.0.6 to 4.3.9 version. While doing so I am getting following error "javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread". Entire code was working fine earlier but I am getting this error just because of library upgrade. Hibernate version which is being used in my project is 4.3.7.
Here is my applicationcontext.xml
<beans default-autowire="byName"
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:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="premier" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#***:1521:PSPRODDB" />
<property name="username" value="**" />
<property name="password" value="*****" />
</bean>
<!-- entity manager configuration -->
<!--<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- externals -->
<bean id="mapperFactory" class="org.dozer.spring.DozerBeanMapperFactoryBean">
<property name="mappingFiles" value="classpath*:dozerBeanMapping.xml" />
</bean>
</beans>
My Entity class
#SuppressWarnings("serial")
#Entity
#Table(name = "ROLES")
#NamedQueries({
#NamedQuery(name = "RoleModuleMO.findById", query = "SELECT rolemodule FROM RoleModuleMO rolemodule WHERE rolemodule.id =:id");
#Proxy(lazy = false)
#EntityListeners(value = SimpleAuditListener.class)
public class RoleModuleMO implements Serializable, SimpleAuditable {
public RoleModuleMO () {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "ROLEMODULE_ID_SQ")
#SequenceGenerator(name = "ROLEMODULE_ID_SQ", sequenceName = "ROLEMODULE_ID_SQ")
private Long id;
#Column(name="ISVISIBLE", nullable = false)
private Boolean isVisible;
#Column(name="ISENABLED", nullable = false)
private Boolean isEnabled;
#Column(name="NAME", nullable = false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getVisible() {
return isVisible;
}
public void setVisible(Boolean visible) {
isVisible = visible;
}
public Boolean getEnabled() {
return isEnabled;
}
public void setEnabled(Boolean enabled) {
isEnabled = enabled;
}
}
My Service class
#Transactional
public class RolesServiceImpl implements RolesService {
#PersistenceContext
private EntityManager em;
private RolesDAO rolesDAO;
private Mapper mapper;
public RolesServiceImpl(RolesDAO rolesDAO, Mapper mapper) {
this.rolesDAO = rolesDAO;
this.mapper = mapper;
}
#Transactional
public RoleModule saveOrUpdateModule(RoleModule vo) {
RoleModuleMO mo = new RoleModuleMO();
mapper.map(vo, mo);
mo = rolesDAO.saveOrUpdateModule(mo);
vo = mapper.map(mo, RoleModule.class);
return vo;
}
}
This was working in spring 4.0.6 and after upgrading to 4.3.9 facing the error.
Is there any changes made by spring community which is causing this error or there is something else which I need to change?
Thanks in advance.
I need to configure spring + JPA (EntityManager) + Hibernate .
If I had to fetch = FetchType.LAZY run server success
If I had to fetch = FetchType.EAGER run server error:
I using tomcat 7
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: fmis2] Unable to build EntityManagerFactory
...
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: fmis2] Unable to build EntityManagerFactory
...
Caused by: org.hibernate.loader.MultipleBagFetchException: can not simultaneously fetch multiple bags
Please help me. Where I was wrong.
Thanks
Config applicationContext.xml
<context:annotation-config />
<context:component-scan base-package="com.evnit.fmis" />
<jpa:repositories base-package="com.evnit.fmis" />
<!-- START -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/jpa-persistence.xml" />
<property name="persistenceUnitName" value="fmis2" />
<property name="dataSource" ref="fmis2dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect" />
<property name="database" value="SQL_SERVER" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="fmis2dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
jpa-persistence.xml
<persistence-unit name="fmis2" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jar-file>/WEB-INF/lib/accounting-inf-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/masterdata-inf-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/congno-backend-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/congcudungcu-backend-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/taisan-backend-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/vattu-backend-1.0-SNAPSHOT.jar</jar-file>
<jar-file>/WEB-INF/lib/muahang-backend-1.0-SNAPSHOT.jar</jar-file>
</persistence-unit>
Java Code entity
package com.evnit.fmis.accounting.entity;
#Entity
#Table(name = "ChungTu", schema = "ketoan")
public class ChungTu implements java.io.Serializable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "chungTu")
public List<DinhKhoan> getDinhKhoans() {
return this.dinhKhoans;
}
public void setDinhKhoans(List<DinhKhoan> dinhKhoans) {
this.dinhKhoans = dinhKhoans;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "chungTu")
public List<Uynhiemchi> getUynhiemchis() {
return this.uynhiemchis;
}
public void setUynhiemchis(List<Uynhiemchi> uynhiemchis) {
this.uynhiemchis = uynhiemchis;
}
}
#Entity
#Table(name = "DinhKhoan", schema = "ketoan")
public class DinhKhoan implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private ChungTu chungTu;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "IdChungtu")
public ChungTu getChungTu() {
return this.chungTu;
}
public void setChungTu(ChungTu chungTu) {
this.chungTu = chungTu;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "dinhKhoan")
public List<HoaDonVat> getHoaDonVats() {
return this.hoaDonVats;
}
public void setHoaDonVats(List<HoaDonVat> hoaDonVats) {
this.hoaDonVats = hoaDonVats;
}
}
#Entity
#Table(name = "Uynhiemchi", schema = "ketoan")
public class Uynhiemchi implements java.io.Serializable {
private ChungTu chungTu;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "IdChungtu", nullable = true)
public ChungTu getChungTu() {
return this.chungTu;
}
public void setChungTu(ChungTu chungTu) {
this.chungTu = chungTu;
}
}
#Entity
#Table(name = "HoaDonVAT", schema = "ketoan")
public class HoaDonVat implements java.io.Serializable {
private DinhKhoan dinhKhoan;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "IdDinhKhoan")
public DinhKhoan getDinhKhoan() {
return this.dinhKhoan;
}
public void setDinhKhoan(DinhKhoan dinhKhoan) {
this.dinhKhoan = dinhKhoan;
}
}
Java Code Dao
public abstract class CommonDao {
#PersistenceContext(unitName = "fmis2")
protected EntityManager entityManager;
}
The problem is the Hibernate specification: he doesn't allow more than one list noted with EAGER. There are some options to bypass this problem:
Use LAZY lists. If you need 'to simulate' a eager relation, use yourList.size() to populate before the query;
Use Set instead List in your data structures.
Other explanations:
Hibernate cannot simultaneously fetch multiple bags
Multiple fetches with EAGER type in Hibernate with JPA
Regards.
I have such method
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void rceive(MySerializableObject message) {
log.info("received: {}", message);
}
and such config on xml
<jms:annotation-driven />
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${brokerURL}" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="5" />
<property name="maximumActiveSessionPerConnection" value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="pooledConnectionFactory" />
Seems consumer was not created. I can send messages but cannot receive them.
Any idea whats wrong here?
Just tested your config and it works well. Only difference that I make a class with #JmsListener as a <bean> in that context:
<bean class="org.springframework.integration.jms.JmsListenerAnnotationTests$TestService"/>
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class JmsListenerAnnotationTests {
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private TestService testService;
#Test
public void test() throws InterruptedException {
this.jmsTemplate.convertAndSend("myQName", "foo");
assertTrue(this.testService.receiveLatch.await(10, TimeUnit.SECONDS));
assertNotNull(this.testService.received);
assertEquals("foo", this.testService.received);
}
public static class TestService {
private CountDownLatch receiveLatch = new CountDownLatch(1);
private Object received;
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void receive(String message) {
this.received = message;
this.receiveLatch.countDown();
}
}
}
<jms:annotation-driven /> makes the #JmsListener infrastructure available, but to force Spring to see those methods your classes should be beans anyway.
For example <component-scan> for the package with #Service classes.
Cheers!
My config:
<bean parent="cache-template">
<property name="name" value="yagoLabel" />
<property name="cacheMode" value="PARTITIONED" />
<property name="atomicityMode" value="TRANSACTIONAL" />
<property name="distributionMode" value="PARTITIONED_ONLY" />
<property name="backups" value="1" />
<property name="store">
<bean class="id.ac.itb.ee.lskk.lumen.yago.YagoLabelCacheStore" autowire="byType" init-method="init" />
</property>
<property name="writeBehindEnabled" value="true" />
<property name="writeBehindFlushSize" value="102380" />
<property name="writeBehindFlushFrequency" value="30000" />
<property name="writeBehindBatchSize" value="10240" />
<property name="swapEnabled" value="false" />
<property name="evictionPolicy">
<bean class="org.gridgain.grid.cache.eviction.lru.GridCacheLruEvictionPolicy">
<property name="maxSize" value="102400" />
</bean>
</property>
</bean>
And I start GridGain as follows:
My GridCacheStore implementation:
public class YagoLabelCacheStore extends GridCacheStoreAdapter<String, YagoLabel> {
private static final Logger log = LoggerFactory
.getLogger(YagoLabelCacheStore.class);
private DBCollection labelColl;
#GridSpringResource(resourceName="mongoDb")
private DB db;
#Inject
private GridGainSpring grid;
#PostConstruct
public void init() {
log.info("Grid is {}", grid);
labelColl = db.getCollection("label");
}
I start GridGain as follows:
String entityId = "Muhammad";
try (AnnotationConfigApplicationContext appCtx
= new AnnotationConfigApplicationContext(LumenConfig.class)) {
Grid grid = appCtx.getBean(Grid.class);
GridCache<String, YagoLabel> labelCache = YagoLabel.cache(grid);
log.info("Label for {}: {}", entityId, labelCache.get(entityId));
}
LumenConfig Spring configuration contains a DB bean named mongoDb.
However this throws NullPointerException because db is not injected properly. I tried #Inject GridGainSpring just for testing, and even GridGainSpring itself is not injected.
I also tried setting <property name="db" ref="mongoDb"/> in the GridGain Config XML but Spring complains cannot find the bean.
My workaround is to put it inside a public static field but that's soo hacky: https://github.com/ceefour/lumen-kb/blob/b8445fbebd227fb7ac337c758a60badb7ecd3095/cli/src/main/java/id/ac/itb/ee/lskk/lumen/yago/YagoLabelCacheStore.java
The way is to load the GridConfiguration using Spring, then pass it to GridGainSpring.start() :
// "classpath:" is required, otherwise it won't be found in a WAR
#ImportResource("classpath:id/ac/itb/ee/lskk/lumen/core/lumen.gridgain.xml")
#Configuration
public static class GridGainConfig {
#Inject
private ApplicationContext appCtx;
#Inject
private GridConfiguration gridCfg;
#Bean(destroyMethod="close")
public Grid grid() throws GridException {
return GridGainSpring.start(gridCfg, appCtx);
}
}
:-)
Our current development based on Discriminator model in Multi-tenancy. Following is the technical stack we are currently engage with,
Spring 3.1.1.RELEASE
Hibernate 4.1.6.Final
We are maintain tenant id by keep one column separately in each table. Tenant id filter when session is created.
Example model class.
#Entity
#FilterDef(name = "tenantFilter", parameters = #ParamDef(name = "tenantIdParam", type = "string"))
#Filters(#Filter(name = "tenantFilter", condition = "tenant_id = :tenantIdParam"))
#Table(name = "assessment")
public class Assessment implements java.io.Serializable, Comparable<Assessment> {
private static final long serialVersionUID = -2231966582754667029L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#Column(name = "tenant_id", nullable = false, updatable = false)
private String tenantId;
// rest of the code...
}
This is the configuration of session factory
<!-- create database connection pool -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="jdbc:mysql://${jdbc.host}:3306/${jdbc.database}?createDatabaseIfNotExist=true&autoReconnect=true&useUnicode=true&connectionCollation=utf8_general_ci&characterEncoding=UTF-8" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="idleConnectionTestPeriodInMinutes" value="60"/>
<property name="idleMaxAgeInMinutes" value="240"/>
<property name="maxConnectionsPerPartition" value="30"/>
<property name="minConnectionsPerPartition" value="10"/>
<property name="partitionCount" value="3"/>
<property name="acquireIncrement" value="5"/>
<property name="statementsCacheSize" value="100"/>
<property name="releaseHelperThreads" value="3"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="lk.gov.elg.orm.model"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.hbm2ddl.auto=update
</value>
</property>
</bean>
<bean id="tenantBasedSessionFactory" class="lk.gov.elg.orm.dao.impl.TenantBasedSessionFactoryImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Tenant base session factory
public class TenantBasedSessionFactoryImpl implements TenantBasedSessionFactory {
private SessionFactory sessionFactory;
#Override
public Session getTenantBasedSession(Object tenantId) {
Session session = sessionFactory.openSession();
session.enableFilter("tenantFilter").setParameter("tenantIdParam", tenantId);
return session;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getAllTenantBasedSession() {
Session session = sessionFactory.openSession();
return session;
}
}
sample Service class
#Service("assessmentService")
public class AssessmentServiceImpl implements AssessmentService {
#Autowired
private AssessmentDao assessmentDao;
public Long saveAssessment(Assessment assessment, Object tenantId) {
return assessmentDao.saveAssessment(assessment, tenantId);
}
}
Sample DAO class
#Repository("assessmentDao")
public class AssessmentDaoImpl implements AssessmentDao {
#Autowired
private TenantBasedSessionFactory tenantBasedSessionFactory;
public Long saveAssessment(Assessment assessment, Object tenantId) {
Session session = tenantBasedSessionFactory.getTenantBasedSession(tenantId);
try {
session.beginTransaction();
session.save(assessment);
session.getTransaction().commit();
return assessment.getId();
} catch (HibernateException e) {
logger.error("Error in persist assessment:", e);
session.getTransaction().rollback();
return null;
} finally {
session.close();
}
}
}
I would like to know is there a way to get the spring transaction support with this Discriminator model for database transactions ?
And the other thing is I would like to know is there any advantage of give transaction handling to spring rather than handling it by our side?
Thanks in advance.
i had similar problem and i have resolved it using aspect instead of customizing sessionfactory, so i can leverage annotation driven transaction support
Below code is for aspect which works on annotation #Tennant
#Aspect
public class TennantAspect {
#Autowired
private SessionFactory sessionFactory;
#Around("#annotation(Tennant)")
public Object enableClientFilter(ProceedingJoinPoint pjp) throws Throwable {
Object obj;
boolean isDAO=(pjp.getTarget() instanceof BaseDAO<?,?>);
try {
if(isDAO){
Authentication auth=SecurityContextHolder.getContext().getAuthentication();
if(auth!=null){
User user=(User) auth.getPrincipal();
this.sessionFactory.getCurrentSession().enableFilter("clientFilter").setParameter("clientId", user.getClientId());
}
}
obj=pjp.proceed();
}finally {
if(isDAO)
this.sessionFactory.getCurrentSession().disableFilter("clientFilter");
}
return obj;
}
}
Hope this solves your problem.
Alternatively you can also look at tenancy support by hiberante & spring
http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html
https://github.com/mariofts/spring-multitenancy