Spring #Transactional annotation is not rollback when second table insertion have exception - java

I want to insert two different table consecutively in a service class , i am using Spring JDBCTemplate.My code are.
#Transactional(rollbackFor=Exception.class,propagation= Propagation.REQUIRES_NEW)
public boolean insertRestorent(User user) throws GenericException {
logger.println(IMessage.INFO, new StringBuilder(CLASS_NAME).append("::insertRestorent() restorent registration start"));
user.setUserType(USER_TYPE.RESTORENT.ID);
User checkUser = userDao.checkUserByEmail(user.getEmail());
if (checkUser != null) {
GenericException exception = new GenericException();
exception.setMessage("Shop already registered!!");
throw exception;
}
long getuserUid=userDao.insertUser(user);
long retoId= restorentdao.insertRestorent(user, getuserUid);
return false;
}
My transactionManager code is
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" />
</bean>
My problem is when userDao.insertUser(user); is successful but if there is any exception in restorentdao.insertRestorent(user, getuserUid); then fist user table is not rollback.
Exceptions are.
SEVERE: Servlet.service() for servlet [MyDelivery] in context with path [/MyDelivery] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL []; Field 'shop_description' doesn't have a default value; nested exception is java.sql.SQLException: Field 'shop_description' doesn't have a default value] with root cause
java.sql.SQLException: Field 'shop_description' doesn't have a default value
userDao insert method is.
public long insertUser(User user) {
logger.println(IMessage.INFO, new StringBuilder(CLASS_NAME).append("::insertUser()"));
final String query = "INSERT INTO user(user_type,email,mobile_no,password,name,status,created_on)VALUES(?,?,?,?,?,?,now());";
KeyHolder keyHolder = new GeneratedKeyHolder();
getJdbcTemplate().update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(query, PreparedStatement.RETURN_GENERATED_KEYS);
int i = 1;
ps.setLong(i++, user.getUserType());
ps.setString(i++, user.getEmail());
ps.setString(i++, user.getMobileNo());
ps.setString(i++, user.getPassword());
if (!StringUtils.isEmpty(user.getName())) {
ps.setString(i++, user.getName());
} else {
ps.setString(i++, null);
}
ps.setInt(i++, STATUS.INACTIVE.ID);
return ps;
}
}, keyHolder);
user.setUserId(keyHolder.getKey().longValue());
return keyHolder.getKey().longValue();
}

Related

javax.persistence.PersistenceException: org.hibernate.TransactionException: Already have an associated managed connection

I am using JPA + Hibernate in my application. I try to insert 10000 data in mysql DB. But i got this error. How to solve ?
javax.persistence.PersistenceException: org.hibernate.TransactionException: Already have an associated managed connection
Caused by: org.hibernate.TransactionException: Already have an associated managed connection
//For loop call 10000 times in getMdmId() method and pass MdmId object each time.
//#PersistenceContext
#PersistenceContext(type = PersistenceContextType.TRANSACTION)
private static EntityManager em = getEntityManager();
public static String getMdmId(MdmId mdm) {
String mdmId = "";
EntityTransaction tr = em.getTransaction();
try {
if (!tr.isActive())
tr.begin();
em.persist(mdm);
mdmId = Long.toString(mdm.getId());
// tr.commit();
if (tr.isActive()) tr.commit();
else em.flush();
} catch (Exception error) {
logger.error(error.getMessage());
if (tr.isActive()) tr.rollback();
error.printStackTrace();
}
System.out.println("----"+mdmId);
return mdmId;
}
private static EntityManager getEntityManager() {
return Persistence.createEntityManagerFactory("sql-connection").createEntityManager();
}

Executing one bean before another

Right now I have the following two beans defined in my controller which are connecting to two different databases. The two beans in the current scenario
are executing at the same time as soon as controller receives doc_id and empID_ from the webservice call. I don't want this to happen.
CURRENT SCENARIO:
#RequestMapping(value="/showEmpDetails", method=RequestMethod.GET)
public String showEmpDetails
(
#RequestParam(value="doc_id", defaultValue="0") String doc_id,
#RequestParam(value="emp_id", defaultValue="1234") String empID_
)
{ String jsonString = "";
try {
// Bean 1 defined for an Oracle database
EmployeeDao empDao = (EmployeeDao) context.getBean("empDao");
// Bean 2 defined for different Oracle database
EmployeeDao empForValueDao = (EmployeeDao) context.getBean("empValueDao");
List<ShowDescription> beanONE = empForValueDao.getemployeeDetails(empID_);
List<ShowDescription> beanTWO = empDao.getCompanyShowDocument(doc_id);
}
return JsonString;
}
EXPECTED SCENARIO:
I want the beanONE to execute first, return a value back in the controller after calling the getemployeeDetails method and then pass that value at
the time of second bean call next to doc_id as shown below. In this manner the SQL query I have used inside the getCompanyShowDocument method can make
use of this value and get me the results I am expecting.
#RequestMapping(value="/showEmpDetails", method=RequestMethod.GET)
public String showEmpDetails
(
#RequestParam(value="doc_id", defaultValue="0") String doc_id,
#RequestParam(value="emp_id", defaultValue="1234") String empID_
)
{ String jsonString = "";
try {
// Bean 1 defined for different Oracle database
EmployeeDao empForValueDao = (EmployeeDao) context.getBean("empValueDao");
// Bean 2 defined for an Oracle database
EmployeeDao empDao = (EmployeeDao) context.getBean("empDao");
List<ShowDescription> beanONE = empForValueDao.getemployeeDetails(empID_);
List<ShowDescription> beanTWO = empDao.getCompanyShowDocument(doc_id,returnedValue);
}
return JsonString;
}
In XML I have the beans defined as follows :
<bean id="empDao" class="abc.edg.myproj.dao.impl.EmployeeDaoImpl">
<property name="dataSource"><ref bean="DB_ONE"/></property>
</bean>
<bean id="empValueDao" class="abc.edg.myproj.dao.impl.EmployeeDaoImpl">
<property name="dataSource"><ref bean="DB_TWO"/></property>
</bean>
Is it possible to achieve what I described above? Please advise. Thanks
Adding getemployeeeDetails method below as requested in the comment:
public List<ShowDescription> getemployeeDetails(String empID) throws DaoException
{
DataSource ds = null;
Connection conn = null;
PreparedStatement pstmt2 = null;
ResultSet rs2 = null;
List<ShowDescription> employeeValue = new ArrayList<ShowDescription>();
try {
ds = jdbcTemplate.getDataSource();
conn = ds.getConnection();
pstmt2 = conn.prepareStatement(selectValueSQL);
pstmt2.setString(1, empID);
rs2 = pstmt2.executeQuery();
while(rs2.next()) {
ShowDescription employeeRecord = new ShowDescription();
employeeRecord.setEmployeeValue(rs2.getString("value"));
employeeValue.add(employeeRecord);
System.out.println("I am inside getemployeeDetails method to get the cvtermPropValue");
System.out.println(employeeRecord.getEmployeeValue()); // Prints the value
}
} catch(Throwable th) {
//some code
} finally {
// Closing all resources here
}
return employeeValue;
}

Multithreading with Spring JDBC

I am having a connection issue with Spring JDBC and an SQL database. The problem is that on the first try, my method creates n amount of threads, they query the database, and no issues occur. If I immediately run the method again, same thing - no issues. Note I am not restarting the application between tries.
The issue occurs when I wait for a few minutes before running the application again - so I assume there is a timeout issue somewhere, or threads are being abandoned.
And the catch is, when I run this method using a single threaded version it works perfectly fine. So I believe the actual URL /user/pass/driver setup is ok. I am new to multithreading so I think there is a flaw somewhere in my implementation.
I am using Spring JDBC with Apache Tomcat JNDI connection pooling:
Java Multithreading:
public List<Item> getSetPoints(List<Item> items) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(Item item: items) {
executorService.submit(new ProcessItem(item));
}
executorService.shutdown();
}
class ProcessItem implements Runnable {
private Item item;
public ProcessItem(Item item) {
this.item = item;
}
public void run() {
Item newItem = piDAO.retrieveSetPoint(item);
}
}
DAO:
#Component("PIDAO")
public class PIDAO {
private NamedParameterJdbcTemplate jdbc;
#Resource(name="pijdbc")
public void setPiDataSource(DataSource jdbc) {
this.jdbc = new NamedParameterJdbcTemplate(jdbc);
}
public Item retrieveSetPoint(Item item) {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("tag", item.getTagName());
String sql = "SELECT TOP 1 time, value, status FROM piarchive.picomp2 WHERE tag = :tag AND status=0 AND questionable = false ORDER BY time DESC";
try {
return jdbc.queryForObject(sql, params, (rs, rowNum) -> {
item.setPiDate(rs.getString("time"));
item.setPiValue(rs.getString("value"));
return item;
});
} catch (Exception e) {
System.out.println(e);
}
}
}
Spring DAO Container:
<jee:jndi-lookup jndi-name="jdbc/PI" id="pijdbc"
expected-type="javax.sql.DataSource">
</jee:jndi-lookup>
JNDI Configuration:
<Resource
name="jdbc/PI"
auth="Container"
type="javax.sql.DataSource"
maxTotal ="25"
maxIdle="30"
maxWaitMillis ="10000"
driverClassName="com.osisoft.jdbc.Driver"
url="**Valid URL**"
username="**Valid Username**"
password="**Valid Password**"
/>
Stacktrace when error occurs:
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [SELECT TOP 1 time, value, status FROM piarchive.picomp2 WHERE tag = ? AND status=0 AND questionable = false ORDER BY time DESC]; SQL state [null]; error code [0]; [Orb.Channel] The channel is not registered on server.; nested exception is java.sql.SQLException: [Orb.Channel] The channel is not registered on server.
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:645)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:680)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:707)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:757)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:211)
at btv.app.dao.PIDAO.retrieveSetPoint(PIDAO.java:36)
at btv.app.service.PiService$ProcessItem.run(PiService.java:91)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLException: [Orb.Channel] The channel is not registered on server.
at com.osisoft.jdbc.PreparedStatementImpl.executeQuery(PreparedStatementImpl.java:167)
at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:82)
at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:82)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:688)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:629)
... 11 more
error : java.sql.SQLException: [Orb.Channel] The channel is not registered on server, is unique to the particular driver I am using.
After some research it essentially means that the connection to the server was dropped - however, looking at the logs on the SQL server indicate it was not dropped on the server side.
Try creating a fresh NamedParameterJdbcTemplate within the retrieveSetPoint method to see if this eliminates any problems you're having with timeouts
#Component("PIDAO")
public class PIDAO {
private DataSource jdbc;
#Resource(name="pijdbc")
public void setPiDataSource(DataSource jdbc) {
this.jdbc = jdbc;
}
public Item retrieveSetPoint(Item item) {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("tag", item.getTagName());
String sql = "SELECT TOP 1 time, value, status FROM piarchive.picomp2 WHERE tag = :tag AND status=0 AND questionable = false ORDER BY time DESC";
try {
return (new NamedParameterJdbcTemplate(jdbc)).queryForObject(sql, params, (rs, rowNum) -> {
item.setPiDate(rs.getString("time"));
item.setPiValue(rs.getString("value"));
return item;
});
} catch (Exception e) {
System.out.println(e);
}
}
}
Alternatively, you can reuse the NamedParameterJdbcTemplates and refresh them when they time out; this can benefit from explicit pooling, e.g.
private final int poolSize = 10;
public Collection<Item> getSetPoints(List<Item> items) {
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Queue<Item> queue = new ConcurrentLinkedQueue<>();
queue.addAll(items);
Collection<Item> output = new ConcurrentLinkedQueue<>();
for(int i = 0; i < poolSize; i++) {
executorService.submit(new ProcessItem(queue, output);
}
return output;
}
class ProcessItem implements Runnable {
private final Queue<Item> queue;
private final Collection<Item> output;
private NamedParameterJdbcTemplate jdbc;
public ProcessItem(Queue<Item> queue, Collection<Item> output) {
this.queue = queue;
this.output = output;
this.jdbc = piDAO.getNamedJdbcTemplate();
}
public void run() {
Item item = null;
while((item = queue.poll()) != null) {
try {
output.add(piDAO.retrieveSetPoint(item, jdbc));
} catch(SQLException e) {
this.jdbc = piDAO.getNamedJdbcTemplate();
output.add(piDAO.retrieveSetPoint(item, jdbc));
}
}
}
}
#Component("PIDAO")
public class PIDAO {
private DataSource jdbc;
#Resource(name="pijdbc")
public void setPiDataSource(DataSource jdbc) {
this.jdbc = jdbc;
}
public NamedParameterJdbcTemplate getNamedJdbcTemplate() {
return new NamedParameterJdbcTemplate(jdbc);
}
public Item retrieveSetPoint(Item item, NamedParameterJdbcTemplate template) throws SQLException {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("tag", item.getTagName());
String sql = "SELECT TOP 1 time, value, status FROM piarchive.picomp2 WHERE tag = :tag AND status=0 AND questionable = false ORDER BY time DESC";
return template.queryForObject(sql, params, (rs, rowNum) -> {
item.setPiDate(rs.getString("time"));
item.setPiValue(rs.getString("value"));
return item;
});
}
}

Spring Jdbc template setAutocommit(false)

I want insert multiple rows in db table.
I am using SpringJdbc.
How can i manage transaction in SpringJdbc connection.
My code is:
#Override
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public int addUserRelationshipMapping(final ArrayList<UserDO> userDOs, final long userId) throws UserDataException {
final JdbcTemplate jd = this.getJdbctemplate();
try {
jd.getDataSource().getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
int totalAdded = 0;
try {
int[] isAdded = jd.batchUpdate(ADD_USER_RELATION_MAPPING, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
final long userRelationId = jd.queryForObject(USER_RELATION_KEY, Long.class);
UserDO userDO = userDOs.get(i);
ps.setLong(1, userRelationId);
ps.setLong(2, userId);
ps.setLong(3, userDO.getprimaryUserId());
ps.setInt(4, 1);
ps.setInt(5, 0);
jd.getDataSource().getConnection().commit();
}
#Override
public int getBatchSize() {
return userDOs.size();
}
});
totalAdded = isAdded.length;
} catch (DuplicateKeyException dExp) {
log.info("error for duplicate key exception ",dExp);
log.error(dExp);
} catch (DataAccessException dExp) {
throw new UserDataException("error while adding user relation for userId is" + userId, dExp);
}
return totalAdded;
}
In this code userRelationId return always old values not updated table values.
So will use database connection commit.
SOF question:Java MYSQL/JDBC query is returning stale data from cached Connection
I got error message:
Caused by: java.sql.SQLException: Can't call commit when autocommit=true
So i need help for this.
Advance thanks.
See the example of how to set auto commit false
<bean id="database" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
...
<property name="defaultAutoCommit" value="false" />
...
</bean>
</property>
ref:
spring 3.1: jdbcTemplate auto commit to false.
You can also try the annotation based connection transaction management ?
How can I config to turn off autocommit in Spring + JDBC?

Register stored procedure out parameters using javax.persistence.EntityManager

I am using EntityManager for database operations. I want to execute stored procedure using this EntityManager. I am using below code to execute the procedure but don't know how to register for In/Out parameters.
Query query = appsEntityManager.createNativeQuery("{call test(?,?,?)}");
query.setParameter(1, "");
query.setParameter(2, "");
query.setParameter(3, "");
query.getResultList();
Please help to solve this.
Is theren't any way to achieve this problem?
Try this implementations:
Obtain a java.sql.Connection using your EntityManager:
Connection cc = ((SessionImpl) em.getDelegate()).connection();
With this Connection you can use the java.sql.CallableStatement class to make calls to stored procedures and functions, by this way:
CallableStatement callableStatement;
try {
callableStatement = cc.prepareCall("{call stored_proc(?,?,?,?,?)}");
callableStatement.setString(1, "1");//Parameter #1
callableStatement.setString(2, "ET");////Parameter #2
callableStatement.setString(3, "|s|");// //Parameter #3
callableStatement.registerOutParameter(4, Types.INTEGER); //Output # 1
callableStatement.registerOutParameter(5, Types.VARCHAR); //Output # 2
callableStatement.execute();
Integer outputValue = callableStatement.getInt(4);
String outputValue1 = callableStatement.getString(5);
}
Another implementation is based on this post.
Create a class that extends StoredProcedure:
/**
* Class to provide access to the database. With this class you can invoke functions and stored procedures.
*/
public class GenericDatabaseCaller {
/**Data source. */
private DataSource dataSource;
/**
* This method requires LinkedHashMaps for inParams and outParams so that parameters can be set in a
* sequence.
* #param functionName Name of the stored procedure or function.
* #param isFunction indicates if the process to execute is a Function or a Stored procedure.
* #param inParams {#link LinkedHashMap} of IN parameters.
* #param outParams {#link LinkedHashMap} of OUT Parameters.
* #return {#link Map} with the output parameters.
*/
public Map executeSimpleProcedure(String functionName, boolean isFunction, Map<String, Object> inParams,
Map<String, Object> outParams) {
InnerStoredProcedure innerStoredProcedure = new InnerStoredProcedure(dataSource, functionName, isFunction,
inParams, outParams);
return innerStoredProcedure.executeProcedure(inParams);
}
private class InnerStoredProcedure extends StoredProcedure {
/**
* #param ds
* #param SQL
* #param isFunction
* #param inParams
* #param outParams
*/
public InnerStoredProcedure(DataSource ds, String SQL, boolean isFunction, Map<String, Object> inParams, Map<String, Object> outParams) {
setDataSource(ds);
setFunction(isFunction);
setSql(SQL);
configerParameters(inParams, outParams);
compile();
}
/**
* Configure the input and output parameters for the stored procedure
* #param inParams
* #param outputParamers
*/
public void configerParameters(Map<String, Object> inParams, Map<String, Object> outputParamers) {
if (inParams != null && inParams.size() > 0) {
Iterator<String> keySetIterator = inParams.keySet().iterator();
while (keySetIterator.hasNext()) {
String key = keySetIterator.next();
if (inParams.get(key) instanceof String) {
declareParameter(new SqlParameter(key, Types.VARCHAR));
} else if (inParams.get(key) instanceof Integer) {
declareParameter(new SqlParameter(key, Types.INTEGER));
} else if (inParams.get(key) instanceof Date || inParams.get(key) instanceof java.sql.Date) {
declareParameter(new SqlParameter(key, Types.DATE));
}
// TODO Add more types.
}
}
if (outputParamers != null && outputParamers.size() > 0) {
Iterator<String> keySetIterator = outputParamers.keySet().iterator();
while (keySetIterator.hasNext()) {
String key = keySetIterator.next();
if (outputParamers.get(key) instanceof String) {
declareParameter(new SqlOutParameter(key, Types.VARCHAR));
} else if (outputParamers.get(key) instanceof Integer) {
declareParameter(new SqlOutParameter(key, Types.INTEGER));
}
}
}
}
public Map executeProcedure(Map inputs) {
return execute(inputs);
}
}
}
Then, you can invoke your function or stored procedure:
String procedureName = "stored_proc";
Map<String, Object> inMap = new LinkedHashMap<String, Object>();
inMap.put("parameter1", "10");
inMap.put("parameter2", "|Lib");
inMap.put("parameter3", "P");
Map<String, Object> outMap = new LinkedHashMap<String, Object>();
outMap.put("output", 0);
outMap.put("output1", "");
Map resultMap = genericDatabaseCaller.executeSimpleProcedure(procedureName, inMap, outMap);
To instantiate GenericDatabaseCaller we add some lines to our application-context.xml
<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="genericDatabaseCaller"
class="co.com.custom.GenericDatabaseCaller">
<property name="dataSource" ref="dataSource" />
</bean>
Note that the data source is the same that we use to instantiate the EntityManager.
Then in our class we use the annotation #Respository and add the #Autowired annotation to the GenericDatabaseCaller field.
#Repository(value = "customDao")
public class JPACustomDao implements CustomDao {
/** entity manager. */
private EntityManager em = null;
/**
* Sets the entity manager.
*
* #param entityManager {#link EntityManager}.
*/
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.em = entityManager;
}
#Autowired
private GenericDatabaseCaller genericStoredProcedure;
}
I hope this works for you.
Carefull with the retrieval of connection from the hibernate session. One might be getting a second connection outside of the current transaction.
Also it is better to use the Session inteface instead of its implementation.
#PersistenceContext
private EntityManager em;
#Transactional
#Override
public String create(final JpaPojo pojo) throws SQLException {
ReturningWork<Integer> work = new ReturningWork<Integer>() {
#Override
public String execute(Connection con) throws SQLException {
CallableStatement call = con.prepareCall("{?= call MyFunction(?,?,?)}");
call.registerOutParameter(1, Types.INTEGER);
call.setString(2, pojo.getFooId());
(...)
call.execute();
return call.getString(1);
}
}
Session session = (Session) em.getDelegate();
return session.doReturningWork(work);
}

Categories

Resources