I'm using Spring JdbcTemplate on one of my projects and now, when there are really very much requests with it - I started to face this exception:
org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback;
uncategorized SQLException for SQL [{? = call API.get_data_for_nb(?, ?)}];
SQL state [99999]; error code [17009]; Closed Statement;
nested exception is java.sql.SQLException: Closed Statement
So the Closed Statement exception is received when you try to execute statement that is already closed, but in my case I don't close it by myself - I use JdbcTemplate exactly for that. So, firstly, what could be the reason for that?
The JdbcTemplate object itself is contained in #Stateless EJB in this way:
#Stateless(name = "NbEdwServiceEJB")
public class NbEdwServiceBean implements NbEdwServiceLocal, NbEdwServiceRemote {
#Resource(mappedName = JNDI)
private DataSource dataSource;
private static volatile JdbcTemplate jdbcTemplate;
#PostConstruct
protected void construct() {
synchronized (NbEdwServiceBean.class) {
if (jdbcTemplate == null) {
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
}
}
}
private String getDataFromDB(final String request, final int isDigitalSignVerified) {
String response = null;
try {
response = jdbcTemplate.execute(SQL_GET_DATA, new CallableStatementCallback<String>() {
public String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.registerOutParameter(1, Types.VARCHAR);
cs.setInt(2, isDigitalSignVerified);
cs.setString(3, request);
cs.executeUpdate();
return cs.getString(1);
}
});
} catch (DataAccessException ex) {
LOGGER.error("getDataFromDB()", ex);
}
return response;
}
}
I know that this is maybe not the strictly right way to do it, I could just create instance of JdbcTemplate for every stateless bean - so I might do just that. So, secondly, why is this ever happening? My suppose was that JdbcTemplate's execute method isn't thread safe, but can someone give the full explanation on what is going on?
I have JEE version 5 running on WebLogic 10.3.5 if it's matter.
#Tolegen Izbassar I'm sorry that you're stuck with EE5.
Concerning the Singleton and EE5 there were some alternatives out there. One is vendor specific extensions, for example JBoss 5.x had service beans providing Singleton+JMX. A second solution is to use a earlier version of Jboss Seam compatible with EE5. A third alternative is to use the ServerContext from the Servlet API.
What you're trying to do in #PostConstuct is definitely not good. Non final statics in SLSB is a no go.
I suggest to have a look, at section 29.3 from Spring framework reference which describes EJB - Spring integration, an example from that section:
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
public class MyFacadeEJB implements MyFacadeLocal {
// automatically injected with a matching Spring bean
#Autowired
private MyComponent myComp;
// for business method, delegate to POJO service impl.
public String myFacadeMethod(...) {
return myComp.myMethod(...);
}
Related
I want to call different methods that interact with my database in one method.
something like this :
#Autowired
EnteteService es; // service for jpaRepository
#Autowired
SystemOracleServiceJpa so; // service using jdbcTemplate
#Autowired
DetailService ds; // service for jpaRepository
#Transactional
public void configure(EnteteAR entete) throws ConfigurationException {
try{
this.es.save(entete); // first methode
List<DetailAR> details = this.so.getStructure(entete.getTableName());
if(details.size()>0){
this.ds.saveAllDetails(details); // second
this.so.CreateTable(details, entete.getTableName(), "DEM");//third
this.so.createPkIdxDem(entete.getTableName()); // fourth
this.so.CreateTable(details, entete.getTableName(), "BACK"); // fifth
}
else{
throw new ConfigurationException("Configuration error");
}
}catch(Exception e){
throw new ConfigurationException(e.getMessage());
}
}
I want to commit only if no errors appears in all this methods inside my main method "configure".
I was thinking that #transactionnal annotation work for this, but that commit after each method inside.
Exemple :
if this.es.save work and this.ds.saveAllDetails dont, I find data of es.save on database :(
Someone can help my please ?
thank with advance for your reading and your potential help.
#Transactional will automatically invoke a rollback if an unchecked exception is thrown from the executed method.
ConfigurationException in your case is a checked exception and hence it does not work.
You can make it work by modifying your annotation to
#Transactional(rollbackOn = ConfigurationException.class)
public void configure(EnteteAR entete) throws ConfigurationException {
try{ ....
Now the project is using springmvc+ spring + mybatis + druid + postgresql
The users in the project correspond to the users in the database, so each time you run SQL, you switch the users with the (set role user) command and then perform the crud operations of the database.
My question:
Because there are many connections in the connection pool, the first step is to get the connection of the database, then switch users, and then perform the operation of business SQL on the database. But I don't know which part of the project this logic should be processed, because the connection of the connection pool and the execution of SQL are implemented by the underlying code. Do you have any good plans?
Can you provide me with a complete demo, such as the following operations:
Step 1, get the user's name from spring security (or shiro).
Step 2, Get the connection currently using the database from the connection pool.
Step 3, execute SQL (set role user) to switch roles.
Step 4, perform crud operation.
Step 5, Reset the database connection(reset role)
Here is a simple way to do what you need with the help of mybatis-spring.
Unless you already use mybatis-spring the first step would be to change the configuration of your project so that you obtain SqlSessionFactory using org.mybatis.spring.SqlSessionFactoryBean provided by mybatis-spring.
The next step is the implementation of setting/resetting the user role for the connection. In mybatis the connection lifecycle is controlled by the class implementing org.apache.ibatis.transaction.Transaction interface. The instance of this class is used by the query executor to get the connection.
In a nutshell you need to create your own implementation of this class and configure mybatis to use it.
Your implementation can be based on the SpringManagedTransaction from mybatis-spring and would look something like:
import org.springframework.security.core.Authentication;
class UserRoleAwareSpringManagedTransaction extends SpringManagedTransaction {
public UserRoleAwareSpringManagedTransaction(DataSource dataSource) {
super(dataSource);
}
#Override
public Connection getConnection() throws SQLException {
Connection connection = getCurrentConnection();
setUserRole(connection);
return connection;
}
private Connection getCurrentConnection() {
return super.getConnection();
}
#Override
public void close() throws SQLException {
resetUserRole(getCurrentConnection());
super.close();
}
private void setUserRole(Connection connection) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
Statement statement = connection.createStatement();
try {
// note that this direct usage of usernmae is a subject for SQL injection
// so you need to use the suggestion from
// https://stackoverflow.com/questions/2998597/switch-role-after-connecting-to-database
// about encoding of the username
statement.execute("set role '" + username + "'");
} finally {
statement.close();
}
}
private void resetUserRole(Connection connection) {
Statement statement = connection.createStatement();
try {
statement.execute("reset role");
} finally {
statement.close();
}
}
}
Now you need to configure mybatis to use you Transaction implementation. For this you need to implement TransactionFactory similar to org.mybatis.spring.transaction.SpringManagedTransactionFactory provided by mybatis-spring:
public class UserRoleAwareSpringManagedTransactionFactory implements TransactionFactory {
#Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new UserRoleAwareSpringManagedTransaction(dataSource);
}
#Override
public Transaction newTransaction(Connection conn) {
throw new UnsupportedOperationException("New Spring transactions require a DataSource");
}
#Override
public void setProperties(Properties props) {
}
}
And then define a bean of type UserRoleAwareSpringManagedTransactionFactory in your spring context and inject it into transactionFactory property of the SqlSessionFactoryBeen in your spring context.
Now every time mybatis obtains a Connection the implementation of Transaction will set the current spring security user to set the role.
Best practice is that database users are applications. Application users' access to particular data/resource should be controlled in the application. Applications should not rely on database to restrict data/resource access. Therefore, application users should not have different roles in database. An application should use only a single database user account.
Spring is manifestation of best practices. Therefore, Spring does not implement this functionality. If you want such functionality, you need to hack.
Referring to this, your best bet is to:
#Autowired JdbcTemplate jdbcTemplate;
// ...
public runPerUserSql() {
jdbcTemplate.execute("set role user 'user_1';");
jdbcTemplate.execute("SELECT 1;");
}
I still do not have much confidence in this. Unless you are writing a pgAdmin webapp for multiple users, you should re-consider your approach and design.
I am developing a web application, where, among other things, I need to upload a file to a BLOB column in a mysql table. From what I can see this can be done with JDBC calls (PrepareStatement() etc), but I would like to be able to do this in an EJB class - what I have cobbled together looks like this:
#Stateless
public class ItemsSession {
#PersistenceContext(unitName ="officePU")
private EntityManager em;
private List<Items> itl;
private static final Logger logger=
Logger.getLogger(ItemsSession.class.getName());
...
public String updateDocument(Integer id,InputStream is) throws SQLException{
String msg="";
try{
java.sql.Connection conn = em.unwrap(java.sql.Connection.class);
PreparedStatement pstmt=conn.prepareStatement("UPDATE Documents SET doc = ? WHERE id = ?");
pstmt.setBinaryStream(1, is);
pstmt.setLong(2, id);
pstmt.executeUpdate();
pstmt.close();
}
catch (PersistenceException e){
msg=e.getMessage();
}
return msg;
}
...
}
I have two questions, though:
I would like not to use JDBC directly - is there a way to do this that is 'pure JPA' (edit: not EJB)?
If I have to do it this way, is the PreparedStatement included in the container managed transaction?
Another edit: the code above does the job - I have now tested it. But it isn't pretty, I think.
The first thing you have to do to persist BLOB values the JPA way is you define an entity. The following an example pseodo code:
#Entity
public class Documents {
#Id
private Long id;
#Lob
private byte[] doc;
// .... getters + setters
}
Then you modify your EJB as follows:
#Stateless
public class ItemsSession {
#PersistenceContext(unitName ="officePU")
private EntityManager em;
// ... the rest of your code
public String updateDocument(Integer id,InputStream is) throws SQLException{
String msg = "";
Documents docs = em.find(Documents.class, id); // fetch record from DB
// Assuming your InputStream is a ByteArrayInputStream
byte[] doc = new byte[is.available()]; // create target array
is.read(doc, 0, doc.length); // read bytes into byte array
docs.setDoc(doc); //
return msg; // returning exception message from method call?????
}
...
}
If you don't change the defaults EJB methods are invoked in a transaction by default. So when your method exits, the update should be synchronized with the database.
This answer kann only help you if you read and understand the basics of the JPA. And here is an official tutorial to JPA persistence among other lots of tutorials on the web.
Update
I would like not to use JDBC directly - is there a way to do this that is 'pure JPA'
No.
If I have to do it this way, is the PreparedStatement included in the container managed transaction?
No. But you can use bean managed transaction. If you want to use BMT, the following pseudocode might help you:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class ItemsSession {
#Resource UserTransaction ut;
#Resource DataSource datasource; // you should define datasource on your application server
...
public String updateDocument(Integer id,InputStream is) throws SQLException{
// ...
try (java.sql.Connection conn = datasource.getConnection();
PreparedStatement pstmt=conn.prepareStatement("UPDATE Documents SET doc = ? WHERE id = ?")) {
pstmt.setBinaryStream(1, is);
pstmt.setLong(2, id);
ut.begin();
pstmt.executeUpdate();
ut.commit();
} catch (PersistenceException e){
// ... error handling
}
return ...;
}
...
}
I think you use EJB intergate to with JPA , because you are using this:
#PersistenceContext(unitName ="officePU")
Refernce: http://www.adam-bien.com/roller/abien/entry/ejb_3_persistence_jpa_for
I use Google Guice and jOOQ in my project. Currently I decided to introduce transaction handling using Spring JDBC.
So I did the following.
I set a data source and a transaction manager in Guice module.
#Provides
#Singleton
DataSource provideDataSource(IExternalSettings settings) {
Jdbc3PoolingDataSource dataSource = new Jdbc3PoolingDataSource();
// configuring DataSource
return dataSource;
}
#Provides
#Singleton
DataSourceTransactionManager provideDataSourceTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(new TransactionAwareDataSourceProxy(dataSource));
}
Then I inject my transaction manager to a persistence facade
#Inject
public PersistenceFacade(final DataSourceTransactionManager transactionManager) {
this.dataSource = transactionManager.getDataSource();
this.transactionManager = transactionManager;
}
Later, I use this data source to create jOOQ factory: new Factory(dataSource, ...).
Finaly I run my database access code:
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
// db code in transaction
transactionManager.commit(transaction);
return result;
} catch (Exception e) {
transactionManager.rollback(transaction);
throw e;
}
So far, so good. It works as expected.
So, my next step is to introduce #Transactional annotation using Guice AOP. I created an interceptor
class TransactionalMethodInterceptor implements MethodInterceptor {
#Inject
private DataSourceTransactionManager transactionManager;
#Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
Object result = invocation.proceed();
transactionManager.commit(transaction);
return result;
} catch (Exception e) {
transactionManager.rollback(transaction);
throw e;
}
}
}
And configured it in configure() method of Guice module:
TransactionalMethodInterceptor transactionalMethodInterceptor = new TransactionalMethodInterceptor();
requestInjection(transactionalMethodInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalMethodInterceptor);
And now the issues begin. I can see, using debugger, that control flow reaches interceptor. In particular, it reaches transactionManager.rollback(...) invocation. But the transaction is not actually roll backed.
I have no clue what's the reason. Any ideas? I'll be grateful. Thanks!
I finally managed to come back to this one and I think I found a solution.
First of all, I need to mention I introduced a bit of misunderstanding, when I said it all works without AOP. Today I couldn't reproduce it and I noticed that the connection that is rolled back is different than the one that jOOQ is using. So Alan Vrecko (see comment above) was right!
Then I found this answer and this snippet. I decided to give them a try and it worked! However, it appeared that all previous steps are valid and still need to be there (including Google Guice's interceptor).
The only I change I had to introduce was to remove DataSourceUtils.releaseConnection(con, dataSource); from SpringExceptionTranslationExecuteListener. exception(ExecuteContext ctx). So finally the method looks like
#Override
public void exception(ExecuteContext ctx) {
SQLException ex = ctx.sqlException();
Statement stmt = ctx.statement();
Connection con = ctx.getConnection();
DataSource dataSource = ctx.getDataSource();
JdbcUtils.closeStatement(stmt);
ctx.exception(getExceptionTranslator(dataSource).translate("jOOQ", ctx.sql(), ex));
}
Then all seems to work properly. Thank you all for comments. Of course I'm still open to new/better solutions.
Just a background since I am used to using JDBC since I worked on an old project.
When saving into database, I always set the value to -1 for any unsuccessful insert
public class StudentDao{
//This dao method returns -1 if unsuccessful in saving
public int save(Student stud){
Statement st = con.createStatement();
try{
int val = st.executeUpdate("INSERT student VALUES(.......)");
}catch(Exception e){
return -1;
}
}
}
Based on the return I could could tell if the insert is successful so that I could do the exact logic.
(Tell the user that the transaction is incomplete...)
Now, I used EJB in persisting entity. Most of the tutorials that I am seeing only have this construct.
Netbeans is generating this code also with a 'void' return.
#Stateless
public class StudentFacade{
#PersistenceContext(unitName = "MyDBPU")
private EntityManager em;
public void save(Student student){
em.persist(student);
}
}
When saving entity on a servlet, it just call the method like this code.
#WebServlet(name = "StudentServlet",
loadOnStartup = 1,
urlPatterns = {
"/addStudent",})
public class StudentServlet extends HttpServlet {
#EJB
private StudentFacade studentFacade;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//extract HTTP form request parameters then set
Student stud = Util.getStudent(request);
studentFacade.save(stud);
}
}
But how will I know if the insert is successful? (Dont catch the exception and then just let it propagate.
I have configured my error page so obviously this would catch the error???)
Sorry I am getting confused on integrating my EJB components but I am seeing its benefits.
I just need some advise on some items. Thanks.
The container will propagate the exception to the caller (if you don't do anything with it inside the EJB). That would be probably the SQLException I guess. You can catch it on the servlet and do whatever you want with it. If you use Container Managed Transactions (CMT) the transaction will be rolled back for you automatically by the container and the student object won't be added. As you said, you can of course leave the exception on the web layer as well and then prepare a special error page for it. All depends on your usage scenario.