I'm working with Spring 3 , and I use Hibernate3 as the O/R mapper.While testing my DAO classes I intended to declare my test classes as #Transactional, so that I could rollback the stuff in my DB. DAO classes work fine, however the rollback never happens. this is how my test class looks like:
#BeforeTransaction
public void createTestData() {
// instantiate User objects
}
#Before
public void insertTestData() {
jdbcTemplate.update("INSERT INTO USER VALUES" + user1);
jdbcTemplate.update("INSERT INTO USER VALUES" + user2);
jdbcTemplate.update("INSERT INTO USER VALUES" + user3);
}
#Test
#Rollback(true)
public void testCheckAvailability() {
boolean trueResult = userDao.checkAvailability("shaaadi");
boolean falseResult = userDao.checkAvailability("elthefar");
assertEquals("Invalid output", true, trueResult);
assertEquals("Invalid output", false, falseResult);
}
}
not to forget about the xml file contents:
<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>/database.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="annotatedClasses">
<list>
<value>ir.cms.domain.user.User</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.format_sql=true
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<bean id="userDao"
class="ir.cms.dao.user.impl.UserDaoImpl"
p:sessionFactory-ref="sessionFactory" />
</beans>
Finally , I should add that DAO classes are not transactional , and are only annotated as #Repository.
I appreciate any suggestion :)
My Problem is finally solved. I was using MySQL, and InnoDB mode was off.
Related
If I am returning a Employee object in my controller class, I get a response from my REST service. But if I am trying to return a List, then I frequently get this error:
Error:cannot call senderror() after the response has been committed
(Also atttached the image)
I am using Eclipse, tomcat, maven and making a spring REST service.
Here is my Controller class code snippetwhich is returning Employee, and everything is fine and I can see my JSON data on browser. But when I change Employee to List as my new return type, I receive the following error and my Eclipse also hangs for few minutes. I think my problem could have to do with the JSON convertor. Please help.
#Controller
#RequestMapping("/emp")
public class EMPController {
#Autowired
EmployeeService empservice;
#RequestMapping("byname3/{name}")
#ResponseBody
public Employee getByName(#PathVariable String name) {
System.out.println("Inside getByName()..successfully..EMPController");
.....
......
My dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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/aop http://www.springframework.org/schema/aop/spring-aop-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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.spice.controller" />
<context:component-scan base-package="com.spice.daoImpl" />
<context:component-scan base-package="com.spice.serviceImpl " />
<mvc:annotation-driven /> <!-- tHIS HELPS IN AUTOMATIC JSON-TO-JAVA AND VICE VERSA CONVERTUION -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe"></property>
<property name="username" value="tiwari"></property>
<property name="password" value="tiwari"></property>
</bean>
<bean id="mysessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- eARLIER i WAS USING org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean -->
<!-- BUT THIS LEADS TO SOME CACHE PROVIDER CLASS NOT FOUND EXCEPTION -->
<!-- ALSO THIS AnnotationSessionFactoryBean IS NOW REPLACED BY LocalSessionFactoryBean -->
<property name="dataSource" ref="dataSource"></property>
<property name="annotatedClasses">
<list>
<value>com.spice.beans.Employee</value>
<value>com.spice.beans.Address</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<!-- THIS CLASS IS COMPATIBLE WITH ABOVE VERSIONS OF SPRING AND HIBERNATE -->
<property name="sessionFactory" ref="mysessionFactory" />
</bean>
</beans>
Error:cannot call senderror() after the response has been committed
(Also atttached the image)
This error is an aftermath of a different error. The server encountered a main exception with your application and hence it tried to call HttpServletResponse#sendError(), which throws IllegalStateException in turn.
I am trying to connect to two databases with Spring and JPA. I am already connected to one database(sql server 2012), and I need to connect to another without changing too much stuff. I have app-context-dao.xml file which holds my hibernate and JPA configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<aop:aspectj-autoproxy proxy-target-class="false" />
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/SQLCMS" expected-type="javax.sql.DataSource" />
<jee:jndi-lookup id="dataSourceOracle" jndi-name="jdbc/razmjenskaDBSQL" expected-type="javax.sql.DataSource" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="hr.akd.cms.*" />
<property name="persistenceUnitName" value="caPersistenceUnit" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.SQLServer2012Dialect" />
<property name="showSql" value="false" />
</bean>
</property>
<!-- ..................
Initialize additional Hibernate JPA related properties
if required
.................. -->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<bean id="entityManagerFactoryOracle"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceOracle" />
<property name="persistenceUnitName" value="userPersistenceUnit" />
<property name="packagesToScan" value="hr.akd.cms.*" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.SQLServer2012Dialect" />
<property name="showSql" value="false" />
</bean>
</property>
<!-- ..................
Initialize additional Hibernate JPA related properties
if required
.................. -->
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<!-- .................. Transaction manager setup .................. -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManagerOracle" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryOracle" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- .................. Spring data - declare base packages for scanning,
all classes extending from data repositories will be available for autowire
.................. -->
<jpa:repositories base-package="hr.akd.cms.repository"
factory-class="hr.akd.cms.repository.impl.CMSCustomRepositoryFactoryBean" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
<property name="defaultPersistenceUnitName" value="caPersistenceUnit"/>
</bean>
I added entityManagerFactoryOracle, dataSourceOracle jndi transactionManagerOracle for my second database. When I start my app I got this
Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2: entityManagerFactory,entityManagerFactoryOracle
My factory bean looks like this:
public class CMSCustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
#SuppressWarnings("rawtypes")
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CMSSearchFactoryFactory(entityManager);
}
private static class CMSSearchFactoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public CMSSearchFactoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
#SuppressWarnings("unchecked")
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CMSCustomRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the
// JpaRepositoryFactory
// to check for QueryDslJpaRepository's which is out of scope.
return CMSCustomRepository.class;
}
}
You must make one of the dataSource and entityManagerFactory primary . primary = "true"
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
primary = "true">
We are upgrading our project from Hibernate 3.6.10 to Hibernate 5.2.0 but getting errors while migrating. I have gone through many posts here on StackOverFlow and Google but did not found any solution.
Consider the code reference this website. I only made the database connections are to PostgreSQL database.
If I run this sample after adding these JARs (ALL Added JAR files screenshot) except the hibernate JAR is 3.6.10-final, It works totally fine. But if I make these changes for hibernate 5 upgrade:
Changing JAR file added as Hibernate-core-5.2.1-Final.jar
Changing reference of hibernate3 files to hibernate5(3 replacements; 1 in EmployeeDao import and another 2 in applicationContext.xml)
It throws the following error:
Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1132)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:618)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:615)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:340)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:307)
at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:615)
at hibernate.test.EmployeeDao.saveEmployee(EmployeeDao.java:12)
at hibernate.test.InsertTest.main(InsertTest.java:21)
And I made these changes as well but got the same error:
1. Commenting this in EmployeeDao:
/*HibernateTemplate template;
public void setTemplate(HibernateTemplate template) {
this.template = template;
} */
and extending HibernateDaoSupport, so that it gives sessionFactory setter.
And replacing below code to:
public void saveEmployee(Employee e){
template.save(e);
}
this:
public void saveEmployee(Employee e){
this.getHibernateTemplate().save(e);
}
After debugging I came to know that in HibernateTemplate class provided by Spring-orm-4.3.1 Jar is throwing the mentioned error.
Here is the snapshot.
Can anyone help me out with this :O. We have stuck here for ages. I appreciate help.
create DataSource.xml
<?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"
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">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:properties/database.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
create sessionFactory using the a seperate hibernate.xml file as given below
<?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"
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">
<!-- Hibernate session factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.rapidtech.rapidtechorganic.model.Lot</value>
<value>com.rapidtech.rapidtechorganic.model.ProductAvailable</value>
<value>com.rapidtech.rapidtechorganic.model.Client</value>
<value>com.rapidtech.rapidtechorganic.model.Invoice</value>
<value>com.rapidtech.rapidtechorganic.model.ProductBuyed</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Include both the xml file in your servel-context.xml
<!-- Database Configuration -->
<beans:import resource="classpath:database/DataSource.xml"/>
<beans:import resource="classpath:database/Hibernate.xml"/>
Then in your Abstract class inject sessionFactory
#Resource
private SessionFactory sessionFactory;
public void save(Entity entity){
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(entity);
session.getTransaction().commit();
session.close();
}
I am trying to get a grasp of how to accomplish multi-tenancy with Hibernate, Spring and MySQL. For a first playground example I chose the separate-databases approach: every tenant gets his own database which is named accodingly. Furthermore, another database is used to manage the tenants. The tenant-specific databases hold some staff data. For a first approach, I do not enforce authentication by the user.
I found it really hard to get a comprehensive tutorial on the topic which is why I am currently a bit lost. When I try to deploy Tomcat I get the following messages:
WARN : org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator - Unable to instantiate specified class [sdb.persistence.TenantResolverImpl]
java.lang.ClassCastException: sdb.persistence.TenantResolverImpl cannot be cast to org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider
at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:100)
at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:45)
...
ERROR: org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'staffSessionFactory' defined in ServletContext resource [/WEB-INF/spring/appServlet/spring-data.xml]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to instantiate specified multi-tenant connection provider [sdb.persistence.TenantResolverImpl]
...
Caused by: org.hibernate.service.spi.ServiceException: Unable to instantiate specified multi-tenant connection provider [sdb.persistence.TenantResolverImpl]
at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:104)
at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:45)
This is the structure of my Java classes:
The xml configuration is split across three files. 1. servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<beans:import resource="spring-data.xml" />
<beans:import resource="spring-mvc.xml" />
<context:component-scan base-package="sdb.controller" />
<context:component-scan base-package="sdb.data" />
<context:component-scan base-package="sdb.persistence" />
</beans:beans>
spring-data.xml:
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- connection to the database which holds tenant-management information -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/allTenants" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="tenantsSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>sdb.domain.Tenant</value>
</list>
</property>
</bean>
<bean id="tenantTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="tenantsSessionFactory" />
</bean>
<!-- not quite sure if this is right -->
<bean id="staffSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan" value="sdb.domain" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.default_schema">public</prop>
<prop key="hibernate.multiTenancy">SCHEMA</prop>
<prop key="hibernate.tenant_identifier_resolver">sdb.persistence.ConnectionProviderImpl</prop>
<prop key="hibernate.multi_tenant_connection_provider">sdb.persistence.TenantResolverImpl</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="staffSessionFactory" />
</bean>
</beans>
spring-mvc.xml:
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:default-servlet-handler />
<mvc:annotation-driven />
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix"><value>/WEB-INF/pages/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
Now to the Java classes. My DAOs each have a session factory field:
#Repository
public class TenantDaoImpl implements TenantDao {
#Autowired
private SessionFactory tenantsSessionFactory;
...
#Repository
public class StaffDaoImpl implements StaffDao {
#Autowired
private SessionFactory staffSessionFactory;
The controller:
#Controller
public class HomeController {
#Autowired
StaffDao staffDao;
#RequestMapping(value = "/{companyName}/{staffId}", method = RequestMethod.GET)
public String google(Model model, #PathVariable String companyName, #PathVariable String staffName) {
List<Staff> staffs = staffDao.getStaff();
// Yeah, I know that this functionality should actually reside in the Dao or a service layer
Optional<Staff> foundStaff = staffs.stream().filter(staff -> staff.getSurName().equals(staffName)).findFirst();
if (foundStaff.isPresent()) {
model.addAttribute("foreName", foundStaff.get().getForeName());
model.addAttribute("surName", foundStaff.get().getSurName());
}
model.addAttribute("companyName", companyName);
return "staff";
}
}
Finally, the multi-tenancy related source code. TenantResolverImpl.java:
public class TenantResolverImpl implements CurrentTenantIdentifierResolver {
#Override
public String resolveCurrentTenantIdentifier() {
if (FacesContext.getCurrentInstance() != null) {
// I don't know if this will work - the line was copied from somewhere else
return FacesContext.getCurrentInstance().getExternalContext().getRequestServerName();
}
return null;
}
// Not sure what to do with this method...
#Override
public boolean validateExistingCurrentSessions() { return true; }
}
ConnectionProviderImpl.java:
public class ConnectionProviderImpl implements MultiTenantConnectionProvider {
private Map<String, ComboPooledDataSource> dataSources;
#Autowired
TenantDao tenantDao;
#Override
public Connection getAnyConnection() throws SQLException {
// should probably return the connection for 'dummy' or so
return getConnection("google");
}
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
if (tenantDao.containsTenantWithName(tenantIdentifier)) {
if (!dataSources.containsKey(tenantIdentifier)) {
ComboPooledDataSource dataSource = new ComboPooledDataSource("Example");
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
} catch (PropertyVetoException e) {
e.printStackTrace();
return null;
}
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/" + tenantIdentifier);
dataSource.setUser("root");
dataSource.setPassword("root");
dataSources.put(tenantIdentifier, dataSource);
}
return dataSources.get(tenantIdentifier).getConnection();
}
return null;
}
...
}
Due to my (yet) superficial understanding of the technologies, I guess there are plenty of errors/shortcomings in the code. Feel free to criticize anything noteworthy.
Hopefully you figured it out yourself, if not here is my hint:
Change the properties and you should be done ;)
<prop key="hibernate.tenant_identifier_resolver">sdb.persistence.ConnectionProviderImpl</prop>
<prop key="hibernate.multi_tenant_connection_provider">sdb.persistence.TenantResolverImpl</prop>
I have a problem to save the data with hibernate from Spring.
When I save data get error:
Exception in thread "main" org.hibernate.HibernateException: save is not valid without active transaction
https://gist.github.com/RuzievBakhtiyor/f3009dbc6a9c31090b59
Spring beans config:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<import resource="hibernate.xml"/>
<import resource="dataSource.xml"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:component-scan base-package="neron" />
Hibernate config bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="neron.models" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
DataSource config bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
</beans>
TestDao:
package neron.dao.Impl;
import neron.dao.TestDao;
import neron.models.Test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
#Transactional
#Repository("testDao")
public class TestDaoImpl implements TestDao {
SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session getCurrentSession()
{
return (Session)sessionFactory.getCurrentSession();
}
public void save(Test test) {
getCurrentSession().save(test);
}
public Test findById(int id) {
return (Test) getCurrentSession().get(Test.class,id);
}
}
You need to do db update operation (insert, update, delete) within transaction boundary .
Session session = getCurrentSession();
Transaction trans = session.beginTransaction(); //begin transaction
//db operation
session.save(test)
trans.commit(); //end transaction
OR
For annotation support, in your spring config bean, add this
<tx:annotation-driven transaction-manager="transactionManager" mode="proxy" proxy-target-class="true" />
Remove this tag from sessionFactory -> hibernateProperties
<property name="hibernate.current_session_context_class">thread</property>