I have a web application (JSF and Managedbeans) using Mybatis without Spring and I want to use Spring in this web application and integrate mybatis with spring.
I have my mybatis-config.xml and the mappers.
I have mappers in xml and interfaces with the same name of the mappers un xml.
Then the classes that implements the interfaces are something like that.
public class LocalidadPersistence implements LocalidadMapper {
#Override
public List<Localidad> getLocalidades() throws SQLException {
List<Localidad> listaLocalidades = null;
SqlSession session = new MyBatisUtil().getSession();
if (session != null) {
try {
listaLocalidades = session.selectList("com.mybatis.mappers.localidad.getLocalidades");
} catch (Exception ex) {
throw new SQLException("Error obteniendo listado Localidades");
} finally {
session.close();
}
} else {
throw new SqlSessionException("Sesion no encontrada");
}
return listaLocalidades;
}}
MyBatisUtil code is this.
public class MyBatisUtil {
private String resource = "com/mybatis/mybatis-config.xml";
private SqlSession session = null;
public SqlSession getSession(){
try{
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
session = sqlMapper.openSession();
}catch (IOException ex){
ex.printStackTrace();
}
return session;
}}
I have been reading something about Mybatis spring integration but I don't understand all very well. I know that my spring xml must be something like that. I have not put the mappersLocation property because I have read that this is not neccesary since i have defined my mappers on mybatis-config.xml.
<context:annotation-config />
<context:component-scan base-package="com" />
<tx:annotation-driven />
<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/DB" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/com/mybatis/mybatis-config.xml" />
<property name="transactionFactory" ref="springManagedTransactionFactory" />
</bean>
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="registroClimaMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.mybatis.dao.RegistroClimaMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
Also I have no idea about how to use this inside my application after it has been configured with spring. What happen with MyBatisUtil class and the implementation of the interfaces?
When you use mybatis-spring, you will get an implementation of mapper interface automatically from the mybatis. So, you can safely use the interface with #Autowired inside any of your spring beans. That's it. Where there is a spring, there a DI. And yes, with mybatis-spring you don't need MybatisUtil at all as all the task are managed in your spring xml .
Related
Multiple data sources in spring
My spring-web.xml is like this
<!-- Data source Bean -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
My DAOImpl is like this
public class BBDAOImpl extends JdbcDaoSupport implements BBDao {
#Autowired
DataSource dataSource;
#Override
public List<Map<String, Object>> getDetails(String customerId) {
String sql = "<SQL Query>";
if(BBUtil.getInstance().isNotEmpty(customerId)) {
try {
return getJdbcTemplate().queryForList(sql,customerId);
} catch (EmptyResultDataAccessException e) {
logger.error("Empty result data - getDetails");
}
} else {
// Want to configure here from second data source
}
return null;
}
Here getJdbcTemplate() method is pointing directly to the AS400 DB(But how). Now, my another requirement has come up. In the else block I want to do some data manipulation from another SQL Server.
Can any one tell me how to configure multiple DB's in this and how to use them?
This project is already developed using XML configuration and now I cannot go back to annotations config.
Thanks in advance guys.
Define two sets of jdbc templates like below:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource2"/>
</bean>
then ahead inject them in repository like below:
#Resource("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
#Resource("jdbcTemplate2")
private JdbcTemplate jdbcTemplate2;
Define two data sources in your spring data configuration file like below:
<!-- Data source Bean 1 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as4002://localhost/BB2" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
and then inject those data sources like below in your repository class and use wherever you want to :
#Autowired
DataSource dataSource;
#Autowired
DataSource dataSource1;
Now use 2nd JDBC template for your if else requirement it will use datasource 2.
You can configure an additional data source like
<bean id="dataSource1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
And then while you want to inject, you can use #Qualifier annotation.
#Autowired
#Qualifier("dataSource1")
DataSource dataSource1;
#Autowired
#Qualifier("dataSource2")
DataSource dataSource2;
Now, whenever in a class you want to use first datasource, you will use variable dataSource1, and when you want to use second datasource, you will have to use variable dataSource2.
However, now you will have to modifiy all the data access layers with #Qualifier annotation, because Spring's will have two datasource candidates to inject to in Datasource, And it is likely to ask you which one to inject.
Below is the way, i am using jdbc connectivity for my spring mvc.
I have some technical doubts, which follows-
1.
As i have invoked datasource object in every bean that requires db connectivity. Is it the right way of doing it?
What if i don't want a particular repository object to be instantiated when the application starts up
(because I'm not sure when user will invoke the object, so why instantiate it at the very beginning)?
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
<property name="minPoolSize" value="${jdbc.minPoolSize}" />
<property name="maxStatements" value="${jdbc.maxStatements}" />
<property name="testConnectionOnCheckout" value="${jdbc.testConnection}" />
</bean>
<bean id="ustestuthenticationRepository"
class="com.test.repository.impl.UstestuthenticationRepositoryImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="someclass"
class="com.test.repository.impl.someclass">
<property name="dataSource" ref="dataSource" />
</bean>
2.
#Qualifier("dbDataSource")
private static DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
and then creating
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
I'm not sure if this is the right way of invoking datasource.If each and every Repository object creates separate jdbctemplate, is it something appropriate?
In every repository class, i am invoking datasource in the folowing way-
Modified Code
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<bean id="someclass" class="com.era.repository.impl.someclass">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
and implementation in someclass is -
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
then only accessing jdbcTemplate variable wherever is required.
Am i doing it correctly now? Please advise.
What you done is not an error. It is possible to work that way.
However.
It means you will simply work with JDBC directly.
It means, you need handle transactions "manually".
It is hard and a lot of code.
You better use spring data and hibernate.
Or spring data and JPA.
Anyway, spring data will help you to manage all resources and will simplify data access for you.
If you already on spring MVC, take also spring data. Why bother?
Official spring data example:
https://spring.io/guides/gs/accessing-data-jpa/
I have been facing the below error while trying to save the object to database. I tried the solution mentioned here1 and here2 but no good. I was following a tutorial but the only difference is versions of Spring and Hibernate.
I am able to persist the object directly using the SessionFactory but it fails with below error if I try this with HibernateDaoSupport
spring.xml
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="xxx" />
</bean>
<context:annotation-config/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="packagesToScan" value="org.sri.sphiber.model"></property>
<property name="hibernateProperties">
<props>
<prop key="dialect">org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="customerDAOImpl" class="org.sri.sphiber.dao.CustomerDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
CustomerDAOImpl.java
public class CustomerDAOImpl extends HibernateDaoSupport {
public boolean insertCustomer(Customer cust){
try {
getHibernateTemplate().saveOrUpdate(cust);
} catch (DataAccessException e) {
e.printStackTrace();
return false;
}
return true;
}
}
Invoke it using.
public class MainClass {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");
CustomerDAOImpl hdi=appContext.getBean("customerDAOImpl",CustomerDAOImpl.class);
Customer customer=new Customer();
customer.setCustomerName("Sri");
boolean isUpdated = hdi.insertCustomer(customer);
}
}
Error message.
Aug 10, 2014 12:45:52 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000232: Schema update complete
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.hibernate4.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1135)
at org.springframework.orm.hibernate4.HibernateTemplate$16.doInHibernate(HibernateTemplate.java:684)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:681)
at org.sri.sphiber.dao.CustomerDAOImpl.insertCustomer(CustomerDAOImpl.java:16)
at org.sri.sphiber.main.MainClass.main(MainClass.java:26)
Version Details :
Spring version : spring-framework-4.0.6.RELEASE
Hibernate Version : hibernate-release-4.3.5.Final
Database : Orcale 11g
You are missing TransactionManager definition, see http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/transaction.html
[UPDATE]
Previously i was writing from my mobile so it was hard to provide details, here is what you need to do:
Spring xml config:
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Add #Transactional annotation to CustomerDaoImpl.insertCustomer method
Now your code should work.
Please note that #Transactional annotation should be used in service layer, not in DAO layer like in this example.
#Transactional annotation tells spring to create proxy which "wraps" annotated method with transaction using aspects.
In configuration file
do the change:-
#Configuration
#EnableTransactionManagement <-----Put this line
public PersistenceConfig{
//your code
}
(OR)
#Bean
#Autowired
public HibernateTemplate getHibernateTemplate(SessionFactory session) {
HibernateTemplate hb = new HibernateTemplate();
hb.setCheckWriteOperations(false);
hb.setSessionFactory(session);
return hb;
}
I have a problem using mappers in mybatis-spring. (Spring Batch)
I need to use a SqlSessionTemplate with ExecutorType in BATCH mode for performance issues (my program must execute thousands of insert statement in a table).
However in my program I need to log errors and updating states in another table of the database and if something goes wrong in the execution of the current step everything is rollback, included the logs, which is not an acceptable behaviour.
I thought I could simple set two different SqlSessionTemplate with differents ExecutorType, but if in my step I use two mappers with different templates I get an exception which says that I can't change ExecutorType during transaction, but I don't know how to solve this issue.
Any help is appreciated. Here some XML configuration.
<!-- connect to database -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource">
<ref local="mainDataSource" />
</property>
</bean>
<bean id="mainDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.pass}" />
</bean>
<bean id="infrastructureSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath*:com/generali/danni/sipo/mdv/dao/mybatis/*Mapper*.xml" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<bean id="infrastructureSqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="infrastructureSqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
<bean id="infrastructureSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="infrastructureSqlSessionFactory" />
</bean>
<bean id="infrastructureAbstractMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"
abstract="true">
<property name="sqlSessionTemplate" ref="infrastructureSqlSessionTemplate" />
</bean>
<bean id="infrastructureAbstractMapperBatch" class="org.mybatis.spring.mapper.MapperFactoryBean"
abstract="true">
<property name="sqlSessionTemplate" ref="infrastructureSqlSessionTemplateBatch" />
</bean>
<bean id="erroriMapper" parent="infrastructureAbstractMapper">
<property name="mapperInterface"
value="com.mdv.dao.ErroriMapper" />
</bean>
<bean id="stagingFileMapper" parent="infrastructureAbstractMapperBatch">
<property name="mapperInterface"
value="com.mdv.dao.StagingFileMapper" />
</bean>
Here i have two mappers, one I'd like to use in BATCH mode, the other in SIMPLE mode.
How can I accomplish this task? Every suggestion is appreciated.
Thanks in advance, and sorry for my bad english.
After a lot of tries, I decided to change my approach to solve this problem.
I defined programmatically a new SqlSessionFactory, generating a new SqlSession with the Batch Executor and I used that one.
Since it is an entirely different SqlSessionFactory, it seems it doesn't give problem if I use 2 differents ExecutorType.
Here a sample working code:
Environment environment = new Environment("TEST", new JdbcTransactionFactory(), dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMappers("com.mdv.dao");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = ssf.openSession(ExecutorType.BATCH);
try {
StagingFileMapper sfm = sqlSession.getMapper(StagingFileMapper.class);
for(Record r : staging){
StagingFile sf = new StagingFile();
//set your sf fields
sfm.insert(sf);
}
sqlSession.commit();
} catch (Exception e) {
//manage exception
}
finally{
sqlSession.close();
}
I'm having some problem with autowire and DI in general, so I hope that someone can help cause I've been stuck for days now.
This is the code:
#Service
public class TicketsController implements Controller {
private TicketManager ticketManager;
#Autowired
public void setTicketManager(TicketManager ticketManager) {
this.ticketManager = ticketManager;
}
...
}
#Service
public class SimpleTicketManager implements TicketManager {
private TicketsDao ticketsDao;
#Autowired
public void setTicketsDao(TicketsDao ticketsDao) {
this.ticketsDao = ticketsDao;
}
...
}
#Repository
public class JdbcTicketDao implements TicketsDao {
private DataSource dataSource;
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource=dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
}
...
}
public final class AppContext {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BeanFactory factory = context;
TicketsController ticketsController = (TicketsController) factory.getBean("ticketsController");
}
...
}
In my beans.xml I've got:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
<context:component-scan base-package="bp.dao" />
<context:component-scan base-package="bp.mvc" />
<context:component-scan base-package="bp.svc" />
<context:component-scan base-package="bp.view" />
This doesn't work and I get:
Error creating bean with name 'jdbcTicketDao': Injection of autowired dependencies failed
... nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [javax.sql.DataSource] found for dependency.`
Can someone please help out with this? What am I doing wrong? It seems that autowiring is working all until the next step where it fails when injecting dataSource.
EDIT: I was playing with the code, and forgot #Autowire before setDataSource() but it is supposed to be there.
Maybe you're missing wiring configuration, try
<context:annotation-config/>
This will be due to the order of bean instance creation. Your DAO has been instantiated before the dataSource instance created.
Keep your data Source bean definition before
other way is , define your dataSource definitions in a separate xml and import that before
Try org.apache.commons.dbcp.BasicDataSource :
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://127.0.0.1:3306/mytckdb?autoReconnect=true"
p:username="user" p:password="pass" />
I use JPA so generally prefer to create EntityManagerFactory and use that
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="PU" />
<property name="jpaVendorAdapter">
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="${database}" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Looks like you are using Spring 2.0 but i think context:component-scan was introduced in Spring 2.5.
Maybe update spring xml-config and spring dependencies to 2.5?
Change
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
to
<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/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
The property is called driverClassName, not driverClass.
Also, you don't need multiple context:component-scan elements You can change
<context:component-scan base-package="bp.dao" />
<context:component-scan base-package="bp.mvc" />
<context:component-scan base-package="bp.svc" />
<context:component-scan base-package="bp.view" />
To
<context:component-scan base-package="bp.dao,bp.mvc,bp.svc,bp.view" />