I use spring to access to a database :
XML Spring context :
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">...</bean>
<bean id="jdbcTmp" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds" />
</bean>
<bean id="myDao" class="MyDao">
<property name="jdbcTemplate" ref="jdbcTmp" />
</bean>
Code :
System.out.println("There is : " + new ClassPathXmlApplicationContext("beans.xml").getBean("myDao").countRowsInTheDB() + " rows in this source";
It's simple and it works good. But I want to choose between different databases at runtime according to a variable.
Something like :
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
int rows1 = ctx.getBean("myDao", "dataSource1").countRowsInTheDB();
int rows2 = ctx.getBean("myDao", "dataSource2").countRowsInTheDB();
What is the simpliest way to do that ?
I want to create an XML configuration for each source :
<bean id="myDao1" class="MyDao"><property name="data" ref="jdbcTmpForDataSource1" /></bean>
<bean id="myDao2" class="MyDao"><property name="data" ref="jdbcTmpForDataSource2" /></bean>
And write the code :
int i = getDataSourceIndex();
ctx.getBean("myDao" + i).countRowsInTheDB();
Create a second datasource, jdbc template, dao :
<bean id="ds2" class="org.apache.commons.dbcp.BasicDataSource">... </bean>
<bean id="jdbcTmp2" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds2" />
</bean>
<bean id="myDao2" class="MyDao">
<property name="jdbcTemplate" ref="jdbcTmp2" />
Create a bean, that is a list of all datasources
Just autowire it in, spring will inject the list
#Autowired List<DataSource> dataSources;
In your code iterate over the list and do whith the datasourceswhatever you want.
Related
I have an Spring MVC 3.2.8 app, and I want to run StandAlone process to generate a PDF. I want to initialize the container and manage beans from an stand-alone app.
I have this piece of code:
public class CreatePDF {
private static final Logger LOGGER = Logger.getLogger (ImportEcolabelToolboxToECAT.class);
public static void main(String[] args) {
String[] configLocations = new String[] {
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/dao/dataAccessContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/webapp/WEB-INF/dao/databaseMessageSource.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/services/impl/servicesContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/webapp/WEB-INF/applicationContext.xml",
"C:/Development/Workspaces/EclipseWS/devices/deviceWeb/src/main/resources/com/nicinc/controller/propertyeditors/propertyeditorsContext.xml"};
FileSystemXmlApplicationContext ctx =
new FileSystemXmlApplicationContext(configLocations, true);
}
}
But I have this error when running the app.
Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
and here the definition from the file dataAccessContext.xml :
<!-- The PropertyPlaceholderConfigurer replaces placeholders in Spring bean definitions with the values from the chosen properties files. -->
<!-- There is an example use in the datasource definition below. Look for the $\{jdbc.*} values. -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/nicinc/dao/jdbc-test.properties</value>
<value>classpath:com/nicinc/dao/dbMessageSource.properties</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</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>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="${hibernate.dialect}"/>
<property name="generateDdl" value="false"/>
<property name="showSql" value="false" />
</bean>
</property>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
The javax.naming package comprises the JNDI Api. Since it is just an API its implementation you will have to provide. Generally the implementation is provided by App server. As per the error you are Missing the JNDI implementation.
Possible solution:
If you dont have any javaee related requirement then you should directly use DriverManagerDataSource.
You need to provide your own implementation.Below link might help.
using application data source locally.
Im using JPA with Hibernate implementation and using JpaTransactionManager to mange transactions.
Below is my application context file
<bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="defaultDataSource" ref="dataSource" />
</bean>
<bean id="entityManagerFactory" primary="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="infra_services" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
I have defined my service class as below
#Service
#Transactional
public class ComponentService {
I execute queries in dao layer as below
Query q = entityManager.createQuery(
"SELECT cc.component FROM "
+ this.typeParameterClass.getSimpleName()
+ " cc WHERE cc.caseload.id = ? ").setParameter(1,
caseloadId);
Collection<Component> ddd =q.getResultList();
for (Component c : ddd) {
System.out.println(c.getComponentId());
System.out.println(c.getComponentRelationships2());
}
return ddd;
I started with select queries. While executing the line System.out.println(c.getComponentRelationships2()); getting could not initialize proxy - no Session] with root cause exception
Not sure why the session is not available here. Please help me on this.
If your service is not in the same context as the one where <tx:annotation-driven /> then it's not working. Because it only look for bean in the same context. Extract from spring doc:
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services. See Section 21.2, “The DispatcherServlet” for more information.
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 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 trying to learn spring framework and bean configuration and so far it seems really cool.
I'm about to create a generic class to include all my Mysql functions and it needs to contain the DataSource. My question is: Is it possible to set the datasource already in the bean configuration?
If not then I'll need to set the class as singleton, create an init function and in the init function to do the following:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
DataSource ds = (DataSource) context.getBean("dataSource");
The question is, instead of doing that, can I 'inject' (donno if that's the right term)
it directly in the bean?
this is my bean configuration.
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/foo"/>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
<property name="validationQuery" value="SELECT 1" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
<property name="initialSize" value="5" />
</bean>
<bean id="bar" class="foo.bar">
<property name="dataSource" value="<HERE_SETTING_THE_DATA_SOURCE_ABOVE>" />
</bean>
Is this possible ?
You can reference a bean like your dataSource.
Your class should have a member that can hold the dataSource:
package mypackage;
public class MyBean {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = data.Source;
}
}
Then you can inject the dataSource bean into this bean:
<beans>
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<!-- set properties -->
</bean>
<bean id="myBean" class="mypackage.MyBean">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
That's it.