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.
Related
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);
I'm trying to update a Json column and completely replace the json. The record is inserted using the PBObject method. But I keep getting the error below.
22-Dec-2020 09:03:15.087 WARNING [http-nio-9095-exec-1] util.server.exception.GenericEclipselinkExceptionHandler.handleException
Internal Exception: org.postgresql.util.PSQLException: ERROR: column "json_col" is of type json but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
Position: 44
Error Code: 0
Call: UPDATE my_table SET JSON_COL = ? WHERE (ID = ?)
bind => [2 parameters bound]
Query: UpdateObjectQuery(service.JsonRecord#7e67b84d)
The update looks like:
PGobject pGobject = new PGobject();
pGobject.setType("json");
pGobject.setValue(record.getJsonDoc());
doInsideTransaction(
-> {
Query query = em.createNativeQuery(
"update table set json_col=? where id=?"
);
query.setParameter(1, pGobject);
query.setParameter(2, record.getId());
query.executeUpdate();
}
);
Which calls
public void doInsideTransaction(UnaryAction<EntityManager> action) {
EntityManager em = entityManagerProvider.get();
runTx(em, () -> action.run(em));
}
public void runTx(EntityManager em, Action action) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
action.run();
transaction.commit();
}
catch (Exception error) {
if (transaction.isActive()) {
transaction.rollback();
}
throw error;
}
}
I've also tried casting the json string using to_json and ::json, but that also returns the same error.
Java 8, Postgres 12.5. Postgres driver 9.4. EclipseLink 2.6.0
I am trying to implement code which will have resultset using simpljdbccall or jdbccall or namedtemplatejdbc
Code will use my stored procedure which is database proc having input parameter and an REF cursor.
I did not found any code which will help me to extract cursor as output to have all multiple rows details in in my resultset using JDBC
DATABASE PROCEDURE
BOOKING_PG.get_infant_info_pr(
c_booking_id IN T_BOOKED_INFANT_INFO.BOOKING_ID%TYPE,
c_booked_infant_details OUT booked_infant_details
)
OPEN c_booked_infant_details FOR
SELECT
BOOKING_ID c_booking_id,
BOOKED_INFANT_INFO_ID c_booked_infant_info_id,
BOOKED_ADULT_PAX_INFO_ID c_booked_adult_pax_info_id,
FROM T_BOOKED_INFANT_INFO T
WHERE T.BOOKING_ID = c_booking_id
and T.STATUS_ID = 1;
JAVA Code
SimpleJdbcCall call = new SimpleJdbcCall(dataSource2)
.withCatalogName("BOOKING_PG")
.withProcedureName("get_infant_info_pr")
.withoutProcedureColumnMetaDataAccess()
.returningResultSet("rs1", new ParameterizedRowMapper() {
#Override
public Object[] mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Object[]{rowNum, rs.getLong("c_booking_id"), rs.getLong(c_booked_infant_info_id) , rs.getLong(c_booked_adult_pax_info_id)};
}
});
SqlParameterSource in = new MapSqlParameterSource()
.addValue(C_BOOKING_ID, bookingId);
Map<String, Object> res = call.execute(in);
List<Object[]> l1 = (List<Object[]>)res.get("rs1");
It is throwing SQL error
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call EURONOVA.BOOKING_PG.GET_INFANT_INFO_PR(?)}]; nested exception is java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'GET_INFANT_INFO_PR'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
I am not sure if there is something wrong in the code or we have to follow some different way to get resultset in simplejdbc call
Can someone please help me in this topic?
First create a class having similar data type and names which your cursor returns.
In your case create a class named BookedInfantDetails.java
public class BookedInfantDetails{
private int bookingId;
private int bookedInfantinfoId;
private int bookedadultpaxinfoid;
public int getBookingId() {
return bookingId;
}
public void setBookingId(int bookingId) {
this.bookingId = bookingId;
}
public int getBookedInfantinfoId() {
return bookedInfantinfoId;
}
public void setBookedInfantinfoId(int bookedInfantinfoId) {
this.bookedInfantinfoId = bookedInfantinfoId;
}
public int getBookedadultpaxinfoid() {
return bookedadultpaxinfoid;
}
public void setBookedadultpaxinfoid(int bookedadultpaxinfoid) {
this.bookedadultpaxinfoid = bookedadultpaxinfoid;
}
}
Now declare in and out parameters in the jdbccall and map the ref cursor to the class using beanproperty rowmapper.
SimpleJdbcCall call = new SimpleJdbcCall(dataSource2)
.withCatalogName("BOOKING_PG")
.withProcedureName("get_infant_info_pr")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("c_booking_id", OracleTypes.Integer),
new SqlParameter("c_booked_infant_details", OracleTypes.CURSOR),
.returningResultSet("c_booked_infant_details",
BeanPropertyRowMapper.newInstance(BookedInfantDetails.class));
SqlParameterSource in = new MapSqlParameterSource()
.addValue(C_BOOKING_ID, bookingId);
Map<String, Object> res = call.execute(in);
List<BookedInfantDetails> l1=
(List<BookedInfantDetails>)res.get("c_booked_infant_details");
Find Our more details on spring docs....
https://docs.spring.io/spring-data/jdbc/old-docs/1.2.1.RELEASE/reference/html/orcl.datatypes.html#orcl.datatypes.ref_cur
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.
I am getting the following error and I am not sure how to resolve it. Any inofrmation would be appreciated.
I am trying to copy data from a MSSQL database to MySQL database using Quartz to schedule the job and Hibernate as my ORM.
Caused by: java.sql.SQLException: Unable to convert between net.sourceforge.jtds.jdbc.DateTime and BINARY.
MySQL Column:
LASTMODIFIEDDATE varchar(24) DEFAULT NULL
MSSQL Column:
[LASTMODIFIEDDATE] [datetime] NULL,
Hibernate Method:
public List findLastUpdatedCases() {
final String ModRestriction = "DATEDIFF(DAY,CONVERT(datetime,LASTMODIFIEDDATE, 110),Getdate())=0";
List<CaseViewGtWy> caseList = getHibernateTemplate().executeFind(
new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Criteria criteria = session.createCriteria(getPersistentClass());
criteria.add(Restrictions.sqlRestriction((ModRestriction)));
return criteria.list();
}
}
);
return caseList;
}
It looks like there was an error (a class mismatch) in my Hibernate DAO. Resolved that and things seem to be working.