I have a small project just to check how everything works. I've implemented the usage of MyBatis and the project just works, I was able to retrieve some data from a database. But right now I need the result to be cached for the 2nd time. I've already tested redis to be an embedded cache manager in spring(cache abstraction: http://static.springsource.org/spring-data/data-redis/docs/current/reference/html/redis.html and liker here: http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html). I've implemented everything and cached one method. BUT!!!
I can't really understand was it cached or not. The first time when I marked up the method, redis said, that there are changes to the db and saved it.. but then I changed the key, and nothing changed... How do I understand, that the method was cached or not?? I will put some code here for you to understand what I'm doing.
Spring Context:
<?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:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:redis="http://www.springframework.org/schema/redis"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
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/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
">
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="file:src/main/java/schema.sql" />
<jdbc:script location="file:src/main/java/test-data.sql" />
</jdbc:embedded-database>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
<context:component-scan base-package="com.mycompany.mybatisproject.serviceimpl" />
<!-- Define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="file:src/main/java/com/mycompany/mybatisproject/persistence/ContactMapper.xml" />
<property name="typeAliasesPackage" value="com.mycompany.mybatisproject.data" />
</bean>
<!-- classpath*:com/mycompany/mybatisproject/persistence/*.xml -->
<!-- Scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mycompany.mybatisproject.persistence" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="localhost" p:port="6379" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:template-ref="redisTemplate" />
</beans>
Implementation of service:
#Service("contactService")
#Repository
#Transactional
public class ContactServiceImpl implements ContactService {
private Log log = LogFactory.getLog(ContactServiceImpl.class);
#Autowired
private ContactMapper contactMapper;
#Cacheable("pacan")
#Transactional(readOnly=true)
public List<Contact> findAll() {
List<Contact> contacts = contactMapper.findAll();
return contacts;
}
}
ContactMapper.xml :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycompany.mybatisproject.persistence.ContactMapper">
<resultMap id="contactResultMap" type="Contact">
<id property="id" column="ID" />
<result property="firstName" column="FIRST_NAME" />
<result property="lastName" column="LAST_NAME" />
<result property="birthDate" column="BIRTH_DATE" />
</resultMap>
<select id="findAll" resultMap="contactResultMap">
SELECT ID, FIRST_NAME, LAST_NAME, BIRTH_DATE
FROM CONTACT
</select>
And finally the main class:
public class App {
private static void ListContacts(List<Contact> contacts) {
System.out.println("");
System.out.println("Listing contacts without details: ");
for (Contact contact : contacts) {
System.out.println(contact);
System.out.println();
}
}
public static void main( String[] args ) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("file:src/main/java/app-context.xml");
ctx.refresh();
ContactService contactService = ctx.getBean("contactService", ContactService.class);
List<Contact> contacts;
contacts = contactService.findAll();
ListContacts(contacts);
}
}
Thanks in advance.
You're caching invocation of ContactServiceImpl.findAll method. For test purpose you can add System.out.println("Method invoked") in findAll method. If the cache works the body of findAll method should be invoked only once, next invocations should read value (result) from cache so you shouldn't see "Method invoked" on console.
Don't use Spring 3.1.0.M1 documentation it is different from 3.1.0.RELEASE: http://static.springsource.org/spring/docs/3.1.0.RELEASE/spring-framework-reference/html/cache.html.
Related
I am having this Spring 4.0 service that copies tables from one schema to another. I am switching between Datasources programmatically by updating the Datasource in an abstract class from which all DAOs inherit.
public abstract class GenericDao implements SystemChangedListener {
private static final Logger logger = Logger.getLogger(GenericDao.class);
private NamedParameterJdbcTemplate jdbcTemplate;
/**
* Initializing the bean with the definition data source through #Autowired
* #param definitionDataSource as instance of #DataSource
*/
#Autowired
private void setDataSource(DataSource definitionDataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(definitionDataSource);
}
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(){
return this.jdbcTemplate;
}
#Override
public void updateDataSource(DataSource dataSource) {
this.setDataSource(dataSource);
}
Whenever I need to switch to anothe DataSource I call this method. It all works fine so far, except for the cases when I have a parent-child tables in a foreign key relationship with foreign key defined as deferrable. I get 'ORA-02292: integrity constraint - child record' found whenever the application tries to delete first the parent table.
This happens in a service by calling the methods like this:
logger.info("Switching to target system: " + repDTO.getSourceSystem());
systemService.switchSystem(targetSys);
logger.info("Cleaning up data in target table: " + repDTO.getTableToReplicate());
// deleting the data
managedTableService.cleanData(repDTO.getTableToReplicate());
logger.info("Importing data in target table: " + repDTO.getTableToReplicate());
// importing the data
managedTableService.importData(inserts);
Methods are executed in a transaction so when something goes wrong I return to the clean state. I also use Hibernate for collecting meta information about my tables, before copying them, but for this I use a different connection.
I tried to call SET CONSTRAINT ALL DEFERRED before the cleaning method, but it doesn't work. I even tried a batch update - SET CONSTRAINT ALL DEFERRED + DELETE FROM TABLE, still no success. What am I doing wrong? Any help is appreciated. Thank you!
PS. I use Oracle 11 as a DB. Here's my spring datasource configuration
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
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/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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- Scans within the base package of the application for #Components to configure as beans -->
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/db.properties" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:packagesToScan="de.telekom.cldb.admin"
p:dataSource-ref="dataSource"
p:jpaPropertyMap-ref="jpaPropertyMap"
p:jpaVendorAdapter-ref="hibernateVendor" />
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="${db.dialect}" />
</bean>
<!-- system 'definition' data source -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${db.driver}"
p:url="${db.url}"
p:username="${db.username}"
p:password="${db.password}" />
<!--
p:maxActive="${dbcp.maxActive}"
p:maxIdle="${dbcp.maxIdle}"
p:maxWait="${dbcp.maxWait}"/>
-->
<util:map id="jpaPropertyMap">
<entry key="generateDdl" value="false"/>
<entry key="hibernate.hbm2ddl.auto" value="validate"/>
<entry key="hibernate.dialect" value="${db.dialect}"/>
<entry key="hibernate.default_schema" value="${db.schema}"/>
<entry key="hibernate.format_sql" value="false"/>
<entry key="hibernate.show_sql" value="true"/>
<entry key="hibernate.connection.SetBigStringTryClob" value="true"/>
<entry key="hibernate.c3p0.min_size" value="5" />
<entry key="hibernate.c3p0.max_size" value="20" />
<entry key="hibernate.c3p0.timeout" value="300" />
<entry key="hibernate.c3p0.max_statements" value="50" />
<entry key="hibernate.c3p0.idle_test_period" value="3000" />
</util:map>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
For switching between DataSources progammatically I use org.apache.commons.dbcp.BasicDataSource
######## UPDATE
Hibernate DEBUG log
I got this exception when I executed this app:
public class MainApp {
private static ApplicationContext context = null;
static SqlSession session = null;
public static void main(String[] args) throws IllegalArgumentException {
try {
context = new FileSystemXmlApplicationContext(
"src/main/webapp/WEB-INF/applicationContext.xml");
session = (SqlSession) context.getBean("sqlSession");
OrderMapper orderMapper = session.getMapper(OrderMapper.class);
int orderQuantity = orderMapper.getAllOrder().size();
System.out.println("Order quantity: " + orderQuantity);
session.commit();
session.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Here is my OrderMapper interface:
public interface OrderMapper {
public List<Order> getAllOrder();
}
OrderMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thonglm1.spring.mappers.OrderMapper">
<resultMap id="result" type="order">
<result property="orderId" column="cartid" />
<result property="type" column="type" />
<result property="saleId" column="saleId" />
<result property="status" column="status" />
<result property="info1" column="info1" />
<result property="info2" column="info2" />
<result property="info3" column="info3" />
</resultMap>
<select id="getAllOrder" resultMap="result">
select cartId, type,
info1
from TRAIN_ORDER;
</select>
</mapper>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#172.21.8.62:1521:SHESVDEV" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="src/main/webapp/WEB-INF/mybatis-config.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.thonglm1.spring.mappers" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
mybatis-config.xml
?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.thonglm1.spring.domain.Order" alias="order" />
</typeAliases>
<mappers>
<package name="com.thonglm1.spring.mappers" />
</mappers>
</configuration>
And finally, the exception I got:
java.lang.IllegalArgumentException: Mapped Statements collection does
not contain value for
com.thonglm1.spring.mappers.OrderMapper.getAllOrder at
org.apache.ibatis.session.Configuration$StrictMap.get(Configuration.java:672)
at
org.apache.ibatis.session.Configuration.getMappedStatement(Configuration.java:507)
at
org.apache.ibatis.session.Configuration.getMappedStatement(Configuration.java:500)
at
org.apache.ibatis.binding.MapperMethod.setupCommandType(MapperMethod.java:240)
at
org.apache.ibatis.binding.MapperMethod.(MapperMethod.java:71)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:39)
at $Proxy6.getAllOrder(Unknown Source) at
MainApp.main(MainApp.java:21)
And 1 more thing, since I quite new to this, is there any bad practices in my code ? Or anything I could improve ? Please feel free to comment, I'd really appreciate it.
Where have you specified the mapper XML location? You could add it in sqlSessionFactory's property mapperLocations. Since you are using maven. I believe it is better to move the mapper XML file to a folder inside resources directory. When You add more number of XML files it will be easier to maintain. I think the below folder structure would make sense as all the related stuff are grouped together with all your JDBC related stuff under one package with subpackages as domain, mappers, service interfaces and service interface implementation.
In this case since all the configurations go inside WEB-INF/classes directory during compile/packaging, which is also a classpath, this application configuration should hold good.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#172.21.8.62:1521:SHESVDEV" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/mybatis-config.xml" />
<property name="mapperLocations" value="classpath*:sqlmap/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.thonglm1.spring.mappers" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
And the mybatis-config.xml, by default all the aliases in the domain package will be class name with the first letter being smaller.
?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.thonglm1.spring.domain" />
</typeAliases>
</configuration>
If the package cannot be read you can try adding the resource directly:
?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.thonglm1.spring.domain.Order" alias="order" />
</typeAliases>
<mappers>
<mapper resource="folderWhereIsXMLMapperIssaved/orderMapper.xml" />
</mappers>
</configuration>
I've been trying to get Hibernate / JPA working with my simple Spring 3.2 REST application.
Hibernate/JPA successfully create my table in MySQL, but the transaction fails once it gets into the Repository -- saying no transaction is in progress. I'm really out of my element here and not even sure how to troubleshoot this as it seems most of the code is happening behind the scenes in xml.
Any help much appreciated.
UPDATE -- jpaContext.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: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.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config />
<context:component-scan base-package="com.saltcitywifi"></context:component-scan>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="punit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" >
<property name="showSql" value="true" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<entry key="hibernate.hbm2ddl.auto" value="create" />
<entry key="hibernate.format_sql" value="true" />
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<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/salt_city_wifi?autoReconnect=true" />
<property name="username" value="wifi_admin" />
<property name="password" value="password" />
</bean>
Controller:
#Controller
public class HotSpotController {
#Autowired
private HotSpotService hotSpotService;
#RequestMapping(value = "/hotSpots", method = RequestMethod.GET)
public #ResponseBody
List<HotSpot> getHotSpots() {
hotSpotService = new HotSpotServiceImpl();
List<HotSpot> spots = hotSpotService.getAllHotSpots();
return spots;
}
#RequestMapping(value = "/hotSpot", method = RequestMethod.POST)
public #ResponseBody
HotSpot addHotSpot(#RequestBody HotSpot hotSpot) {
hotSpotService.addHotSpot(hotSpot);
return hotSpot;
}
}
Service:
#Service("hotSpotService")
#Transactional
public class HotSpotServiceImpl implements HotSpotService {
#Autowired
private HotSpotRepository hotSpotRepository;
private AtomicLong counter = new AtomicLong();
public List<HotSpot> getAllHotSpots() {
List<HotSpot> spots = new ArrayList<HotSpot>();
HotSpot spot;
for (int i = 0; i < 10; i++) {
spot = new HotSpot("location " + i, "http://www.url" + i + ".com",
counter.incrementAndGet());
spots.add(spot);
}
return spots;
}
public HotSpot getHotSpotById(long id) {
HotSpot spot = new HotSpot("New Spot", "New Url", id);
return spot;
}
#Transactional
public HotSpot addHotSpot(HotSpot hotSpot) {
return hotSpotRepository.addHotSpot(hotSpot);
}
}
#Repository("hotSpotRepository")
public class HotSpotRepositoryImpl implements HotSpotRepository {
#PersistenceContext
private EntityManager em;
public HotSpot addHotSpot(HotSpot hotSpot) {
em.persist(hotSpot);
em.flush();
return hotSpot;
}
}
OK I finally figured out what was going on here:
I have two component scanners, one for my controllers in servlet-config.xml and one for my JPA stuff in jpaContext.xml.
They were both scanning basically my whole application:
<context:component-scan base-package="com.saltcitywifi" />
When I forced the component scanner for controllers to only look in the controller package:
<context:component-scan base-package="com.saltcitywifi.controller" />
All of a sudden my requests worked and were processed in the database.
I can only assume since my servlet-config.xml configuration only contains:
<context:component-scan base-package="com.saltcitywifi.controller" />
<mvc:annotation-driven />
That Spring was somehow letting servlet-config.xml register the JPA beans, so the transactional annotations weren't being picked up. This is still confusing to me, so if anyone can help explain why this occurs that would be very helpful. I might just ask another stack overflow question on this subject.
My Java web application on spring framework is using two database say database-1 and database-2. Both database have User table. What I am trying to do is to insert record into both tables at same time.
There are two persistence-unit in the persistence.xml that is pointing out to database.
Here is my persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="p1-jpa" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/MySqlDS2</jta-data-source>
<class>com.xyz.entity.User</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider" />
</properties>
</persistence-unit>
<persistence-unit name="p2-jpa" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/MySqlDS2</jta-data-source>
<class>com.mmxhealthcare.entity.MMASCUser</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider" />
</properties>
</persistence-unit>
</persistence>
Now Whenever I am trying to add user, it is inserted only in Database-1. I am not getting any exception.
Here is my spring-servlet.xml file:-
<?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:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util" xmlns:security="http://www.springframework.org/schema/security"
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/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.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-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.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="order" value="0"/>
</bean>
<!-- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean> -->
<context:property-placeholder location="classpath:config.properties" />
<context:annotation-config />
<context:component-scan base-package="com.xyz.controller" />
<context:component-scan base-package="com.xyz.service" />
<context:component-scan base-package="com.xyz.dao" />
<context:component-scan base-package="com.xyz.security" />
<context:component-scan base-package="com.xyz.dto" />
<context:component-scan base-package="com.xyz.util" />
<context:component-scan base-package="com.xyz.entity" />
<tx:annotation-driven />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="aaentityManagerFactory" />
</bean>
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="mmascentityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="d1SourceLocal" />
<property name="persistenceUnitName" value="p1-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="mmascentityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="d2DataSourceLocal" />
<property name="persistenceUnitName" value="p2-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<!-- <bean id="dataSourceLocal" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/MySqlDS2"/>
</bean> -->
<!-- Local -->
<bean id="d1SourceLocal"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
<bean id="d2DataSourceLocal"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${d2-database.driver}" />
<property name="url" value="${d2-database.url}" />
<property name="username" value="${d2-database.username}" />
<property name="password" value="${d2-database.password}" />
</bean>
</beans>
Here it is my service class:
public class Userservice{
#Autowired
Database2IUserDAO immDao;
#Autowired
Database1IUserDAO iaaDao;
public User saveUser(fname,address){
User u = new User(); // This points Database-1 User table.
u.setFname(fname);
u.setAddress(address);
iaaDao.save(u);
User2 u2 = new User2(); // This points Database-2 User table.
u2.setFname(fname);
u2.setAddress(address);
immDao.save(u2);
}
}
Here is my Database1IUserDAO //This is an Interface
package com.xyz.dao;
public interface Database1IUserDAO {
public Object save(Object ob);
}
Here is my Database2IUserDAO //This is an another Interface for database2
package com.xyz.dao;
public interface Database2IUserDAO {
public Object save(Object ob);
}
** And this is finally DAO class for Database-1 and Database 2**
My both DAO class extends BaseDao class, that have Save() method that we are using to insert or save.
BaseDao.java
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.transaction.annotation.Transactional;
public class BaseDAO {
protected EntityManager entityManager;
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this. entityManager = entityManager;
}
#Transactional
public Object save(Object ob) {
Object object = entityManager.merge(ob);
return object;
}
#Transactional
public void remove(Object ob) {
Object object = entityManager.merge(ob);
entityManager.remove(object);
}
#Transactional
public int update(String query) {
return entityManager.createQuery(query).executeUpdate();
}
}
Please help.
You have to use database specific Transaction Manager as you mentioned in your spring-context.xml file as below.
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="mmascentityManagerFactory" />
</bean>
You should create a save function in your DAO Class with database specific Transaction Manager as follows:
#Transactional(value="transactionManager2")
public Object save(Object ob) {
Object object = entityManager.merge(ob);
return object;
}
I hope it will work for you.
You seem to be using the same EntityManager (EM) from the BaseDAO for all your DAOs. Since you don't specify which TransactionManager the EM is using, it will default to this one:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
... as noted in the documentation http://docs.spring.io/spring/docs/2.0.8/reference/transaction.html (find table 9.2)
If you want to write to another database, you will need to explicity tell Spring which one you want to use. See this answer as an example:
Spring multiple #Transactional datasources
It looks you are using the same datasource for both the persistence unit.
ie java:jboss/datasources/MySqlDS2
so it is inserting into only one DB which is corresponding to above data source mstly Mysql DB.
so add different datasource in both the persistence-unit , and that should help you to insert to both the databases.
it wont give any exception because one datasource that you are using might be valid datasource(i cant guarantee as we dont have data source information in your post).
let me know for any thing else.
My goal is to instanciate the EntityManagerFactory from applicationContext.xml file to get all posts registered in SQL database.
Here's the content of the main files :
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Properties files linkers -->
<context:property-placeholder location="/WEB-INF/resources/database/jdbc.properties"/>
<context:property-placeholder location="/WEB-INF/resources/database/hibernate.properties"/>
<!-- Config database - initialization -->
<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>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Three main layers definition -->
<context:annotation-config />
<context:component-scan base-package="com.zone42"/>
<!-- Transaction sub-system initialization -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
(WEB-INF/classes/)persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="post-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.zone42.model.Post</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
PostDAO.java
public class PostDAO extends GenericDAOEntity<Post> implements IPostDAO
{
}
GenericDAOEntity.java
#Transactional
public class GenericDAOEntity<T> implements IGenericDAO<T>
{
/**
* Properties
*/
#Autowired
#PersistenceContext(unitName="post-unit")
private EntityManagerFactory entityManagerFactory/* = Persistence.createEntityManagerFactory(persistence_unit_name)*/;
//Get all posts
#SuppressWarnings("unchecked")
public List<T> findAll(Class<T> obj) {
EntityManager entityManager = this.entityManagerFactory.createEntityManager();
Query query = entityManager.createQuery("from " + obj.getSimpleName());
return (query.getResultList());
}
/**
* Accessors
*/
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
#PersistenceContext(unitName="post-unit")
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
}
I tried several config combinations but without success. The NullPointerException comes from the findAll method when I want to create an instance of EntityManager from the entityfactory instance. I think I have a configuration problem. I want to precise that the code works whn I instanciate the EntityManagerFactory using operator new directly in the class. Now I just want to allocate my factory choosing another way, the one using xml from appicationContext.xml file. Can anyone help me? Thanks in advance.
You need to either mark the field/setter as #Autowired or explicitly wire the reference up in your XML:
<bean class="PostDAO">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>