how to set SqlMapClient outside of spring xmls - java

I have the following in my xml configurations. I would like to convert these to my code because I am doing some unit/integration testing outside of the container.
xmls:
<bean id="MyMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:sql-map-config-oracle.xml"/>
<property name="dataSource" ref="IbatisDataSourceOracle"/>
</bean>
<bean id="IbatisDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/my/mydb"/>
</bean>
code I used to fetch stuff from above xmls:
this.setSqlMapClient((SqlMapClient)ApplicationInitializer.getApplicationContext().getBean("MyMapClient"));
my code (for unit testing purposes):
SqlMapClientFactoryBean bean = new SqlMapClientFactoryBean();
UrlResource urlrc = new UrlResource("file:/data/config.xml");
bean.setConfigLocation(urlrc);
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:#123.210.85.56:1522:ORCL");
dataSource.setUsername("dbo_mine");
dataSource.setPassword("dbo_mypwd");
bean.setDataSource(dataSource);
SqlMapClient sql = (SqlMapClient) bean; //code fails here
when the xml's are used then SqlMapClient is the class that sets up then how come I cant convert SqlMapClientFactoryBean to SqlMapClient

SqlMapClientFactoryBean is a FactoryBean. It does not implement the SqlMapClient interface itself, but manufactures instances of SqlMapClient which are returned when it's getObject() method is called. The Spring container knows about FactoryBeans, and makes them look just like normal beans from the perspective of the caller. I'm not sure that using the FactoryBean outside a container will work - you may get a "not initialized" exception if you call getObject() outside of the container lifecycle ...
Why not create a separate, stripped down Spring config for your test cases, instantiate that, and obtain the beans from there? Alternatively, you could create the SqlMapClient the non-Spring way and set that on your DAO

SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean();
factory.setConfigLocation(YOUR_SQL_MAP_CONFIG_RESOURCE);
factory.afterPropertiesSet(); //omitting try/catch code
client = (SqlMapClient)factory.getObject();
voila

I want to add what worked for me. Had to work with some legacy code and in till I can transition to MyBatis I want to convert the old applicationContext xml to spring #Configuration classes.
#Bean
public SqlMapClient sqlMap() throws Exception
{
SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean();
factory.setConfigLocation(new ClassPathResource("conf/ibatis.xml"));
DataSource dataSource = this.dataSource;
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return (SqlMapClient) factory.getObject();
}

Related

Spring-Core JNDI Configuration with beans

Lets take I have a class as shown below :
public interface UserDAO {
public List<User> list();
}
public class UserDAOImpl implements UserDAO {
private DataSource dataSource;
public UserDAOImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
Let's assume JNDI configuration is done correctly in tomcat.
Now for spring bean mapping in many sites I found the below configuration:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/UsersDB"/>
</bean>
<bean id="userDao" class="net.codejava.spring.dao.UserDAOImpl">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
</bean>
Here is what my question , UserDAOImpl class is looking for DataSource object but we are injecting JndiObjectFactoryBean object[Which is not a subclass of DataSource] , since we are not even mentioning the factory method how or where the conversion is happening ?
JndiObjectFactoryBean is bean of type org.springframework.beans.factory.FactoryBean. This beans are used as a factory for an object to expose. Below are excerpts from the javadoc for FactoryBean;
Interface to be implemented by objects used within a BeanFactory which are themselves factories for individual objects. If a bean implements this interface, it is used as a factory for an object to expose, not directly as a bean instance that will be exposed itself.
NB: A bean that implements this interface cannot be used as a normal bean. A FactoryBean is defined in a bean style, but the object exposed for bean references (getObject()) is always the object that it creates.
FactoryBeans can support singletons and prototypes, and can either create objects lazily on demand or eagerly on startup. The SmartFactoryBean interface allows for exposing more fine-grained behavioral metadata.
So when spring framework is autowiring the datasource to useDaoImpl it checks whether dataSource is a bean of type FactoryBean which it is in this case so it will assign the object from getObject() method of JndiObjectFactoryBean to the dataSource. If you want to understand this more take a look at ClassPathXmlApplicationContext.finishBeanfactoryInitialization(..) and DefaultListableBeanFactory.preInstantiateSingletons() methods which perform the autowiring in this case.
I don't know how it is done but Spring Frameworks knows that beans is a Factory of DataSource beans, then it can inject the discovered datasource in your others beans.
I you want to know how it is done then you can look at the source code in Spring Framework Github

Does datasource need to be in prototype scope in spring jdbc

When we using spring jdbc , first we define a dataSource bean and inject it when creating jdbcTemplate object . What I want to know is do we need to define this dataSource in prototype scope. Unless there is only one dataSource object for whole application . I think this affects to reduce application performance.
Here is how I have defined dataSouce inside spring configuration file.
<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/testdb" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
In my DAO class I have autowired dataSOurce as below.
#Repository
public class RecordDAOImpl {
JdbcTemplate jdbcTemplate = null;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Let me know what is the best way to define dataSource for spring mvc web application.
What I want to know is do we need to define this dataSource in prototype scope
No we don't need. I guess it wouldn't be good idea, we can use some kind of connection pool datasource and singleton scope bean.
We can also have multiple databases and provide for each own datasource singleton scoped, there is not any problem with that.
Let me know what is the best way to define dataSource for spring mvc web application.
There is nothing wrong with defining your data Sources in xml files (although many devs seem to avoid xml). I like to do it using java config, since I feel like its easier to read.
Depending on driver and database it would look more or less like that:
#Configuration
class DatasourceConfig {
#Bean
DataSource datasource() {
PGPoolingDataSource dataSource = new PGPoolingDataSource();
dataSource.setPassword("pass");
dataSource.setPortNumber(123);
dataSource.setUser("user");
dataSource.setMaxConnections(10);
return dataSource;
}
}

JUnit using HyperSQL to test DAO classes

I did some research on how to test my DAO layer using HyperSQL and I found this question: https://softwareengineering.stackexchange.com/questions/219362/how-to-test-the-data-access-layer
How do I import DBConnection because I tried using hypersql jar and it did not work. In the question from the link I saw this db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true; but I do not know how to use it.
Using JDBC and Spring, in your application you'd be creating a DataSource object, and using that object to create a JdbcTemplate object, which is what you're using to run your JDBC queries. Similar to this example. So in order to use the HSql database, you'd need to change the values used to setup the DataSource bean but only for your unit tests.
Depending on how you're using Spring (annotations, xml config), there's a few ways of doing it. You'll need to create a new configuration bean, or configuration xml file, and then reference it in your unit tests.
For XML, copy over your spring context xml file, and change the DataSource values to something like:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;"/>
<property name="username" value="SA"/>
<property name="password" value=""/>
You'll then need to reference this new context config in your unit test. Something like:
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/test-config.xml"
#ContextConfiguration("/test-config.xml")
public class DAOTests {
#Autowired
private DatabaseDAO dao;
}
With annotations, you'll need to make a new configuration class (you should have one already, a class annotated with #Configuration) and return the appropriate DataSource. Something like this:
#Bean
public DataSource hsqlDataSource(){
return DataSourceBuilder
.create()
.driverClassName("org.hsql.jdbcDriver")
.url("jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;")
.username("SA")
.password("");
.build();
}
You can setup a profile for your configuration (using #Profile("test") annotation), and then specify which profile you want to use in your unit test (#ActiveProfiles("test")). More information here.
Alternatively, you could use the Spring EmbeddedDatabaseBuilder if you're just trying to do a simple DAO Test. There's a tutorial here. It sets you up with a HSql in memory database (as opposed to a file based one), which is very convenient for setting up / tearing down. Just add the bean to your configuration class, and your DAO should pick it up if Spring is injecting the DataSource for you. Example below taken from here
#Bean
public DataSource dataSource() {
// no need shutdown, EmbeddedDatabaseFactoryBean will take care of this
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("db/setup_script.sql")
.build();
}
There's a bit to Spring configuration management. I haven't covered it in much detail in this answer, but look up a few examples and it will start to make sense.

Spring Java Based Configuration

I'm facing issue in converting xml based bean configuration to Java based bean configuration in Spring MVC project. I need to convert the below xml configuration to Java based by using # bean. Please help me.. Thanks in advance
<bean class="someclass">
<property name="somename">
<util:list>
<bean id="someid" class="someotherclass"/>
</util:list>
</property>
</bean>
You need to create the OtherClass bean, like this:
#Bean
private SomeClass someclassBean() {
SomeClass sc = new SomeClass();
sc.setSomeName(Arrays.asList(new SomeOtherClass()));
return sc;
}
In your specific needs, try this:
#Bean
AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter() {
AnnotationMethodHandlerAdapter bean = new AnnotationMethodHandlerAdapter()
bean.setMessageConverters(Arrays.asList(new ByteArrayHttpMessageConverter()));
return bean;
}

Spring declare component in xml

I am mostly using #Autowired and #Component annotations in my project. However, I am going to use DataSource class for database actions.
So, I use this is in my dispatcher-servlet.xml :
<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/market"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
In my dao class, My setter for the dataSource is :
#Autowired
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
However this is not doing the trick. My jdbcTemplateObject is null.
If I dont use "context:component scan ..." and use classical spring beans instead, without utilizing #Autowired annotation, all works good.
I can use my database. However, I dont want to declare all the beans one by one in my xml file. As the project grows, it is not going to be practical. How can I solve this problem ? Is it possible to declare dataSource in my dispatcher-servlet.xml as component, so #Autowired works on dataSource ?
When you use #Autowired on fields Spring will look for dependencies and inject them right there there is no point if setter method here.
You do not need to worry about how spring is going to inject the dependency. It will take care of complete life cycle.
For more on Spring's Dependecy Injection visit this link.
You have annotated the field with #Autowired which tells spring to inject the dependency directly into the field. If you really want to use the setter annotate the setter with #Autowired instead of the field.
#Autowired
public void setDataSource(DataSource ds) { ... }
However I strongly suggest to not create a JdbcTemplate for each bean that needs one (it is quite heavy to create). The JdbcTemplate is a thread-safe object, once constructed. So instead of creating a new one for each bean that needs one (in the setDataSource method) just create a single JdbcTemplateand inject that.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Then in your dao.
#Autowired
private JdbcTemplate jdbcTemplate;
Or what I like to do..
private final JdbcTemplate jdbcTemplate;
#Autowired
public YourRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate=jdbcTemplate;
}
This way you cannot construct an illegal object, whereas with setter based injection you could. While maintaining the possibility to inject one for testing purposes.
Another note, the DriverManagerDataSource is nice for testing but not for production usage, for that use a real connection pool like HikariCP or Tomcat JDBC.

Categories

Resources