Spring Transactional not using same JDBC connection - java

Can someone please tell me why do i sql get exception that "Invalid object #EMP_TEMP" even if i am running both queries under same transaction?
#Transactional
public Map<String, EventType> findEventsByDateRange(final Date startTimestamp, final Date endTimestamp) throws Exception {
log.debug("Fetching Events Data");
String EVENT_QUERY = "Select ID, Name, Status, JoinDate into #EMP_TEMP from EMPLOYEE where JoinDate >= ? and JoinDate < ?";
this.jt.execute(EVENT_QUERY, new PreparedStatementCallback<Boolean>() {
#Override
public Boolean doInPreparedStatement(PreparedStatement preparedStatement) throws SQLException, DataAccessException {
preparedStatement.setTimestamp(1, new java.sql.Timestamp(startTimestamp.getTime()));
preparedStatement.setTimestamp(2, new java.sql.Timestamp(endTimestamp.getTime()));
return preparedStatement.execute();
}
});
//this.jt.execute(EVENT_QUERY);
return this.jt.query("SELECT * from #EMP_TEMP "
, DataExtractor.eventDataExtractor);
}
However if i change code as below then it doesn't complaint. but problem in this approach is that i cannot pass any parameters into first query:
#Transactional
public Map<String, EventType> findEventsByDateRange(final Date startTimestamp, final Date endTimestamp) throws Exception {
log.debug("Fetching Events Data");
String EVENT_QUERY = "Select ID, Name, Status, JoinDate into #EMP_TEMP from EMPLOYEE where JoinDate >= '2015-07-13 00:00:00.000' and JoinDate < '2015-07-14 00:00:00.000'";
/*this.jt.execute(EVENT_QUERY, new PreparedStatementCallback<Boolean>() {
#Override
public Boolean doInPreparedStatement(PreparedStatement preparedStatement) throws SQLException, DataAccessException {
preparedStatement.setTimestamp(1, new java.sql.Timestamp(startTimestamp.getTime()));
preparedStatement.setTimestamp(2, new java.sql.Timestamp(endTimestamp.getTime()));
return preparedStatement.execute();
}
});*/
this.jt.execute(EVENT_QUERY);
return this.jt.query("SELECT * from #EMP_TEMP "
, DataExtractor.eventDataExtractor);
}

Finally i found out that root cause of the problem is not spring but sql server.
In SQL Server 2005, SQL Server 2000, and SQL Server 7.0, the prepared statements cannot be used to create temporary objects and cannot reference system stored procedures that create temporary objects, such as temporary tables. These procedures must be executed directly.
Since i was trying to create the temp table by jdbcTemplate method execute(String sql, PreparedStatementCallback action) which uses prepared statement therefore it was not working.
Instead of that when i created temp table using execute(String sql) it is working.

Related

Mix JOOQ query with JDBC transaction

I have a use case where I would like to mix a jdbc transaction with jooq context.
The JDBC code looks like that:
public void inTransaction(InTransaction lambda) {
DataSource ds = dataSource.get();
try (Connection connection = ds.getConnection()) {
try {
logger.info("set autocommit to false");
connection.setAutoCommit(false);
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
logger.info("commiting transaction");
connection.commit();
}
} catch (RuntimeException e) {
logger.info("rolling back transaction");
connection.rollback();
throw e;
} finally {
logger.info("set autocommit to true");
connection.setAutoCommit(true);
}
} catch (SQLException e) {
throw new TilerException(e);
}
}
#FunctionalInterface
public interface InTransaction {
void execute(Statement statement) throws SQLException;
}
And I would like the lambda parameter to be able to work with both jdbc and jooq.
For jdbc using a statement is pretty straight-forward. For example something like this tutorail:
inTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
In order to execute jooq queries on the same transaction I have to obtain a context. I found an api to get a DSLContext from datasource/connection.
What is not clear to me is if/how to create a jooq DSLContext from a statement?
A solution to the problem you described
You can do all of this with jOOQ's transaction API:
// Create this ad-hoc, or inject it, or whatever
DSLContext ctx = DSL.using(dataSource, dialect);
And then:
public void inJDBCTransaction(InJDBCTransaction lambda) {
ctx.transaction(config -> {
config.dsl().connection(connection -> {
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
}
});
});
}
public void inJOOQTransaction(InJOOQTransaction lambda) {
ctx.transaction(config -> lambda.execute(config.dsl()));
}
#FunctionalInterface
public interface InJDBCTransaction {
void execute(Statement statement) throws SQLException;
}
#FunctionalInterface
public interface InJOOQTransaction {
void execute(DSLContext ctx);
}
Your final code:
inJDBCTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
inJOOQTransaction(ctx -> {
ctx.insertInto(EMPLOYEES).values(106, 20, "Rita", "Tez").execute();
ctx.insertInto(EMPLOYEES).values(107, 22, "Sita", "Singh").execute();
});
I'm not too convinced about the need for this abstraction over jOOQ and JDBC. jOOQ never hides JDBC from you. You can always access the JDBC API as shown above when using the DSLContext.connection() method. So, as shown above:
The jOOQ transaction API does exactly what you're planning to do. Wrap a lambda in a transactional context, commit if it succeeds, rollback if it fails (your version's rollback doesn't work because it catches the wrong exception).
If the "JDBC escape hatch" is needed, jOOQ can offer that
Side note
In many RDBMS, you don't want to run queries on a static JDBC Statement. You'll want to use PreparedStatement instead because:
You'll profit from execution plan caching (and less contention on the cache)
You'll avoid syntax errors (in case your real query is dynamic)
You'll avoid SQL injection trouble
If you want to get the query string from jOOQ you can call
String sqlString = query.getSQL()
and then use this string in your statement:
stmt.executeUpdate(sqlString);

How do I properly return an arraylist that displays data based on userid?

I have been able to properly display an arraylist that displays all data in my MySQL database, however I'm having trouble how to display data based on userid.
Here is my code:
CreditsServlet.java
private void listTrans(HttpServletRequest request, HttpServletResponse response)
throws SQLException, IOException, ServletException {
int id = Integer.parseInt(request.getParameter("id"));
List<Credits> creditlist = OrganizerDao.getAllCredits(id);
for (Credits credits : creditlist) {
if (credits.getOrganizerid() == id) {
request.setAttribute("creditlist", creditlist);
}
request.getRequestDispatcher("credits.jsp").include(request, response);
}
}
Any help or direction will be greatly appreciated. Thanks in advance
EDIT: CreditsDao.java
public static List<Credits> getAllCredits(int id) throws SQLException {
List<Credits> creditlist=new ArrayList<>();
try{
Connection con= OrganizerDao.getConnection();
PreparedStatement ps=con.prepareStatement("select users.id AS organizerid, credits.invoiceid, " +
"credits.amountin AS creditamount, attendees.name AS payee, credits.purpose, gateway.name AS gatewayname " +
"from users, credits, attendees, gateway where credits.userid=users.id and credits.attendeeid=attendees.id " +
"and credits.gateway=gateway.id");
ResultSet rs=ps.executeQuery();
while(rs.next()){
Credits credits = new Credits();
credits.setOrganizerid(rs.getInt("organizerid"));
credits.setInvoiceid(rs.getInt("invoiceid"));
credits.setCreditamount(rs.getDouble("creditamount"));
credits.setPayee(rs.getString("payee"));
credits.setPurpose(rs.getString("purpose"));
credits.setGatewayname(rs.getString("gatewayname"));
creditlist.add(credits);
}
con.close();
}catch(Exception e){e.printStackTrace();}
return creditlist;
}
Way simpler than you think:
First, fix the DAO and use the parameter. Changes below:
public static List<Credits> getAllCredits(int id) throws SQLException {
...
PreparedStatement ps=con.prepareStatement("select users.id AS organizerid, credits.invoiceid, " +
"credits.amountin AS creditamount, attendees.name AS payee, credits.purpose, gateway.name AS gatewayname " +
"from users, credits, attendees, gateway where credits.userid=users.id and credits.attendeeid=attendees.id " +
"and credits.gateway=gateway.id" +
" and users.id = ?"); // Added this extra SQL code
ps.setInt(1, id); // Added this code to use the id parameter
...
}
Then you can simply use the resulting list in the servlet:
private void listTrans(HttpServletRequest request, HttpServletResponse response)
throws SQLException, IOException, ServletException {
int id = Integer.parseInt(request.getParameter("id"));
List<Credits> creditlist = OrganizerDao.getAllCredits(id);
request.setAttribute("creditlist", creditlist);
response.sendRedirect("resultpage.jsp");
}
This code:
Retrieves the parameter id (filtering condition).
Retrieves the credits for the specific user.
It then places them in the attribute creditlist of the request scope.
It finally redirects to the new resulting page, that will show the credits.

Resolving Database Stored XSS issues from Checkmarx

Here I am getting Error at the List<Data> dataList = jdbcTemplate.query location.
Checkmarx says:
AppServices/src/main/java/com/mbusa/app/dao/impl/AppRequestDAOImpl.java
gets data from the database, for the query element. This element’s
value then flows through the code without being properly filtered or
encoded and is eventually displayed to the user in method
getRequestDetail at line 117 of
AppWeb/src/main/java/com/mbusa/app/controllers/AppRequestController.java.
This may enable a Stored Cross-Site-Scripting attack.
List<Data> dataList = jdbcTemplate.query(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
PreparedStatement pstmt = conn.prepareStatement(fSQL);
int index = 1;
for(Object param: effParams) {
pstmt.setString(index, (String) param);
index++;
}
return pstmt;
}
}, new RequestMapper());
public class RequestMapper implements RowMapper<Data> {
#Override
public Data mapRow(ResultSet rs, int rowNum) throws SQLException {
Data data = new Data();
data.setName(rs.getString("name"));
return data ;
}
}
I have added PreparedStatement here to resolve this issue and also tried adding HtmlUtil.htmlEscape upon output data, but still could not resolve this issue.
Can anyone help with this issue?

Getting oracle Stored procedure resultset in java spring mvc

I have the following stored procedure which takes three parameters and returns three ref cursors.
variable id refcursor
variable item refcursor
variable amount refcursor
exec getdata(123,date1,date2, :id, :item, :amount) ;
print id;
print item;
print amount;
i have three resultsets for this stored procedure output. How can i call this in spring mvc and display these three resultsets. I was using the following code to fetch the data through sql query. But now i have developed a stored procedure. so how can i call this SP output insted of my query output.
public Optional<List<student>> getStudentDetails(String id) {
NamedParameterJdbcTemplate parameterJdbcTemplate = new
NamedParameterJdbcTemplate(dataSource);
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue("Id", id);
List<student> studentList =
parameterJdbcTemplate.query(StudentQueryRepository.STUDENT_DETAIL_QUERY,
namedParameters, new studentDecodeRowMapper());
if (studentList.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(studentList);
}
}
Try this:
List<CommunicationContact> campaigns = jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException {
CallableStatement cs = con.prepareCall("{? = call MY_SERVICES.GET_CAMPAIGNS(?,?)}");
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.setString(2, customer);
cs.setString(3, channel);
return cs;
}
},
new CallableStatementCallback<List<CommunicationContact>>() {
#Override
public List<CommunicationContact> doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.execute();
ResultSet rs = (ResultSet) cs.getObject(1);
List<CommunicationContact> communications = commContactRsExtractor.extractData(rs);
return communications;
}
}
);
And the I have a function on the database:
FUNCTION GET_CAMPAIGNS(p_cust IN VARCHAR2,
p_channel IN VARCHAR2)
RETURN SYS_REFCURSOR
IS
l_campaigns_cursor SYS_REFCURSOR;
BEGIN
BEGIN
OPEN l_campaigns_cursor FOR
SELECT...
EXCEPTION
WHEN OTHERS THEN
...
END;
RETURN l_campaigns_cursor;
END GET_CAMPAIGNS;
I believe that a stored procedure with out parameter will also work.

JDBC Template querying

I am trying to execute this method using jdbc template:
public String getClientName(String uuid) {
System.out.println("UUID here in the dao layer is: " + uuid);
String sql = "Select email from client where uuid=?";
return jdbcTemplate.query(sql, new ResultSetExtractor<String>() {
#Override
public String extractData(ResultSet rs) throws SQLException, DataAccessException {
return rs.getString("email");
}
});
}
However I am getting this error:
SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [Select email from client where uuid=?]; nested exception is org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying =?
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Where am I going wrong?
You are not passing the uuid which you got as a parameter to your method. You could do use the api like:
Object sqlParameters[] = {uuid};
return jdbcTemplate.query(sql, sqlParameters,
new ResultSetExtractor<List<String>>() {
#Override
public List<String> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<String> emailList=new ArrayList<String>();
while (rs.next()) {
emailList.add(rs.getString("email"));
}
return emailList;
}});
You're not using your uuid parameter anywhere... I suspect you actually want:
return jdbcTemplate.query(sql, new Object[] { uuid },
new ResultSetExtractor<String>() { ... });
That way uuid will be used as the value for the parameter in your SQL.

Categories

Resources