I have application-context.xml which is having beans like below.
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" >
<property name="jndiName" value="java:/comp/env/DB_NAME" />
</bean>
<bean id="jdbcTemplate" name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
and one context.xml like
<ResourceLink name="DB_NAME1" global="application/cn=MyDB,ou=Database Connections" />
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
If you notice in my context.xml I kept my resource name as DB_NAME1
I also kept default-lazy-init="true" in beans tag at the top of my application-context.xml file. Still I am getting below error
javax.naming.NameNotFoundException: Name [DB_NAME] is not bound in this Context. Unable to find [DB_NAME].
So my question is, How to load my jdbcTemplate/dataSource lazily.
Because in my application some of the services are hitting DB and some are hitting other services. So in case even if DB is down the other services should not stop working.
So I found the temporary solution of my problem.
Wrote new method which is searching for JNDI config and creating jdbcTemplate and if it fails to do so, I am eating the exception.
public JdbcTemplate getJdbcTemplate() {
try {
Context initContext = new InitialContext();
myDataSource = (DataSource) initContext.lookup("java:/comp/env/DBNAME");
myjdbcTemplate = new org.springframework.jdbc.core.JdbcTemplate(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
return jdbcTemplate;
}
This method I am calling every time from my other DAOImpl methods like below
getJdbcTemplate().query("MySp", args, myBeanRowMapper);
Its working for now. But if you see any better approach, please answer here.
Thanks
Related
I am new to spring and working on a sample program using Spring jdbc. this is to check how spring #Trsactional working and rolling back the changes to the Db if there is an exception.
But I am not able to achieve this. Through I am raising an exception in one of the DB update, still it's inserting the data to DB and not rolling back.
I know somewhere I am making mistake but not able to figure it out. Not sure if this is a correct approach.
What I am doing :-
in main methis I am calling load methos of Global class (which has jdbcTemplate as satic member as I will this jdbcTemplate to all other classes)
Global class load methos will initiate the bean using ApplicationContext.
Creating Dbclass instance in main method and sending the jdbcTemplate as parameter.
4.creating some sample data and calling executeDb method.
5.execute DB method will create the instance of other Dbclasss and setting the jdbcTemplate which earlier I initialized using bean in main method (I have separate class for each operation - like createuser, UpdataBalance etc)
then it will call the db opration method to insert data (I am using batchupdate)
EDIT - Removed all try-catch
DB opration code:-
#Transactional(rollbackFor={Exception.class})
public void executeDB(int count) throws Exception
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
//BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
//newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
//newbal.addBalance(); // insert data to db
newacc.getAccList().clear();
newusr.getUserList().clear();
//newbal.getBalanceList().clear();
if(count == 5000)
{
Thread.sleep(1000);
throw new Exception("Rollback");
}
count += 1000;
//throw new Exception();
}
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-xml -->
<context:component-scan base-package="com.example"></context:component-scan>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="startit" class="com.example.springtransaction.GlobalClass">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
You need to throw exception from your method not silently log it in catch block.
And for checked exceptions you need to use #Transactional(rollbackFor = {Exception.class}).
http://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/transactional-roll-back/
https://www.catalysts.cc/en/wissenswertes/spring-transactional-rollback-on-checked-exceptions/
You should remove the try - catch and define that the method throws an Exception. Something like that
#Transactional(rollbackFor={Exception.class})
public void executeDB() throws Exception
{
if(usrlist.size() >= 5)
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
newbal.addBalance(); // insert data to db - raise exception here
}
}
Update
The class that contain the executeDB() method should be a #Component and inject that component in the main class.
Not create a new Dbclass() instance by your own.
In high-level the reason is that the Spring creates proxy classes upon injection for classes that declare #Transactional.
You could read more about Aspect-Oriented Programming here.
Current Situation
I am doing stress load tests using Jmeter on web and rest api server, but some of transations' response time delays a lot so I am using Spring Aspect to get method processing time. What I couldn't set up is that some procedure calls take too much time, so trying to check DB process time(get con, release con, pure db process time) by write logs with the certain transaction. JMX is not an option as I can't track transactions using it. I just want to leave DB pool status with ThreadContext marked on so that I can check the slow transaction and DB pool status at the same time.
Using DB datasource from Tomcat is not considered here as not want to go DB settings within project files.
Using datasource within Spring project is not an option I am considering at the moment.
Current Setup
Spring project's transaction manager uses Tomcat DBCP pool with Oracle datasource(oracle.jdbc.OracleDriver with javax.sql.DataSource)
applicationContext.xml - DB Setting
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/comp/env/jdbc/svc"/>
<property name="resourceRef" value="true"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:../sql/**.xml"/>
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
<bean id="oracleTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
<bean id="transactionManager" class="com.xxx.xxx.api.transaction.TransactionManager">
<property name="transactionManagers">
<list>
<ref bean="oracleTransactionManager"/>
</list>
</property>
</bean>
Trying to do... logging DB pool status
I am trying to use Spring Aspect to write a log whenever functions in certain dao class is called.
The log I want to write is like DB Pool status such as
active connection counts
idle connection counts
max active connection setting
max idle connection setting
and so on.
Question
Is it possible to access Tomcat's db pool from spring project?
Which would have methods something like this in below.
getNumIdle()
getWaitCount()
getNumActive()
You can simply create a proxy to the tomcatJdbcPoolDataSource and use it as the spring bean. I have created a proxy for C3P0 pooled data source. I later create a spring bean of my class with the required config and use it as a datasource. I believe you can do something similar.
public class C3PODataSourceProxy extends AbstractComboPooledDataSource {
public C3PODataSourceProxy() {
super();
}
public C3PODataSourceProxy(boolean autoregister) {
super(autoregister);
}
public C3PODataSourceProxy(String configName) {
super(configName);
}
#Override
public Connection getConnection() throws SQLException {
try {
Connection connection = super.getConnection();
//You can call the below methods and log it, send it to some other class etc
getNumIdleConnections();
getNumBusyConnections();
return connection;
} catch (Exception exception) {
//log the exception
throw exception;
}
}
public Connection getConnection(String username, String password) throws SQLException {
try {
Connection connection = super.getConnection(username, password);
//You can call the below methods and log it, send it to some other class etc
getNumIdleConnections(username, password);
getNumBusyConnections(username, password);
return connection;
} catch (Exception exception) {
//log the exception
throw exception;
}
}
}
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;
}
}
In one of my question asked earlier I got to know that DriverManagerDataSource is NOT intended for production use. So I changed my configuration. I know I am using DBCP which is also outdated and a lot of other connection pools are available like HIkariCP and BOneCP but
I wish to understand the way how to verify that a pool has been setup
or not?
On searching a lot I got some answer at the following link
How would you test a Connection Pool
but I didn't get a way to verify programmatically. Also I cannot debug my jar files used for connection pooling because no source code is available. I dont know why but I can't change my jars for offical reasons.
The following are my configuration (OLD and NEW)
OLD
<bean id="webLogicXADataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="#[csa.db.driver]" />
<property name="url" value="#[csa.db.url]" />
<property name="username" value="#[csa.db.username]" />
<property name="password" value="#[csa.db.password]" />
</bean>
NEW
Using DBCP connection pool
<bean id="webLogicXADataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="#[csa.db.driver]" />
<property name="url" value="#[csa.db.url]" />
<property name="username" value="#[csa.db.username]" />
<property name="password" value="#[csa.db.password]" />
</bean>
OTHER ELEMENTS:(Thus far I have kept them same like they were earlier)
Place holder
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:${DB_PROPERTIES}</value>
</list>
</property>
<property name="placeholderPrefix" value="#[" />
<property name="placeholderSuffix" value="]" />
</bean>
Transaction Manager
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="webLogicXADataSource" />
<qualifier value="inventoryTxManager"/>
</bean>
DAOIMPL SAMPLE BEAN
<bean id="inventoryDao"
class="com.lxnx.fab.ce.icce.inventoryRoutingInvoice.dao.InventoryDaoImpl">
<property name="dataSource" ref="webLogicXADataSource" />
<property name="transactionManager" ref="transactionManager" />
Right now all the DAO classes in my project are singleton(no prototype property set for any of the beans)
The following is the sample java code of the DAOImpl.java class where I need to do all the transactions:
DAOImpl.java
public class InventoryDaoImpl implements InventoryDao {
private final static ISmLog iSmLog = Instrumentation
.getSmLog(LNConstants.SYSTEM_LOG_NAME);
private JdbcTemplate jdbcTemplate;
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dataSource = dataSource;
}
public void setTransactionManager(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
#Transactional private void insertRelatedInfoData(
InventoryModel inventoryModel) {
final List<String> relatedLniList = inventoryModel.getArrRelatedLni();
final String documentLni = inventoryModel.getDocumentLNI();
String sql = "INSERT INTO SCSMD_REPO.INV_RELATED_INFO(LNI, RELATED_LNI) VALUES(?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
String relatedLni = relatedLniList.get(i);
ps.setString(1, documentLni);
ps.setString(2, relatedLni);
}
#Override
public int getBatchSize() {
return relatedLniList.size();
}
});
}
}
I am not getting any errors. Just wanted to verify If a pool has been setup U wish to verify the same
Are all configurations fine or did I miss something?? Please help me out with you valuable answers. thanks
If you don't have logs enable then you can't verify it. however there is one more donkey logic.
every database server will have timeout functionality. if db not hit by application after some time connection will break. for example mysql server will break it's connection from application after 8 hour (if there is no hit from application). you check modify timeout to minimum time (say 30 min)in mysql config file and check after 30 minutes you get connection close exception in you appication, when you hit db
The easiest way, as explained, would be to examine the logs. It's quite likely that a connection pool will log something, at least if your logging level is low enough.
Another way would be to examine the class of the Connection that the datasource returns. If you're dealing with a connection pool, the class will be a wrapper or a proxy class for that pool. The wrapper/proxy class makes sure that when you call close() the connection isn't really closed, it's just returned to the pool for further use. For example if you were to use HikariCP as your pool, you could check if(connection instanceof IHikariConnectionProxy) to see if the pool is being used.
Adding that kind of code in your software would be a bad idea in practically all cases. If you don't know whether a connection pool is being used or not, it's not something you solve with code. It's something you solve by reading and studying more.
You've also named your bean webLogicXADataSource even though nothing seems to support it being an XA datasource. Are you perhaps working on things a bit too advanced for you?
I need a spring datasource like:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="<driver>"/>
<property name="url" value="<url>" />
<property name="username" value="<user>" />
<property name="password" value="<pass>" />
</bean>
I need to obtain driver, url, user, pass from persistence.xml.
Tanks a lot!
Here is my snippet for doign the same, you will obviously have to use your BasicDataSource instead of the ComboPooledDataSource I use. But they are pretty much the same, replace getDriverClass() with driverClassName, apparently.
#Autowired
private ComboPooledDataSource dataSource;
public String myMethod() {
return dataSource.getDriverClass());
}
Do you want to print it, or use it in your application for connecting to the dB?
If later one is the case, then, create a bean for sessionFactory, set hibernateProperties for the same where you can inject datasource as well.
In java code, autowire sessionFactory object (or set it using a setter method) and call getCurrentSession method for the same.
For getting various attributes, use chained getter methods to return datasource and extract all the details.
Let me know if you face any issue or need more details for the same.