I am trying to execute a custom select query in Springboot JPA,
public interface idnOauth2AccessTokenRepository extends JpaRepository<idnOauth2AccessToken, String>,
JpaSpecificationExecutor<idnOauth2AccessToken> {
#Query(value = "select IOCA.userName, IOCA.appName, IOAT.refreshToken, IOAT.timeCreated, IOAT.tokenScopeHash, IOAT.tokenState, IOAT.validityPeriod from idnOauth2AccessToken IOAT inner join idnOauthConsumerApps IOCA on IOCA.ID = IOAT.consumerKeyID where IOAT.tokenState='ACTIVE'")
List<userApplicationModel> getUserApplicationModel();
}
But when I execute I get an error of
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.adl.egw.Model.user.userApplicationModel]
I tried different type of answers from the internet, but I nothing seems to work fine. I also tried implementing a new repository for userApplicationModel but didn't work.
Any answers or implementation which could help.
You are joining columns from different tables and then assigning to a different object. It does not work this way + the userApplicationModel doesn't seem managed entity. For such scenarios, you have to use projection(dto mapping). Take a look of the following Query:
#Query(value = "select new your.package.UserApplicationModelProjection(IOCA.userName, IOCA.appName, IOAT.refreshToken, IOAT.timeCreated, IOAT.tokenScopeHash, IOAT.tokenState, IOAT.validityPeriod)"
+ " from idnOauth2AccessToken IOAT inner join idnOauthConsumerApps IOCA on IOCA.ID = IOAT.consumerKeyID where IOAT.tokenState='ACTIVE'")
List<UserApplicationModelProjection> getUserApplicationModel();
And the class to map to:
public class UserApplicationModelProjection {
private String userName;
private String appName;
private String refreshToken
private OffsetDateTime timeCreated
private String tokenScopeHash;
private String tokenState; //mind the data type
private int validityPeriod; //update the data type
public UserApplicationModelProjection(String userName,
String appName,
String refreshToken,
OffsetDateTime timeCreated,
String tokenScopeHash,
String tokenState,
int validityPeriod)
{
this.userName = userName;
this.appName = appName;
this.refreshToken = refreshToken;
this.timeCreated = timeCreated;
this.tokenScopeHash = tokenScopeHash;
this.tokenState = tokenState;
this.validityPeriod = validityPeriod;
}
// Getters only
}
Check this for detailed explanation: https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/
I have a Spring Boot app with DataTables server-side processing and Oracle database. Actually, I started with implementing one of the tutorials. It worked. The tutorial uses JPA. I want to implement the same using JDBC. I made all the corresponding classes, the repository, the new model with same filds but without jpa. But when I tried to fetch the data, it allowed me to get only the first page without a chance to get to the second page. Below I will post the extracts of the original and added code. So, the original tutorial used these classes:
#Entity
#Table(name = "MYUSERS")
public class User {
#Id
#Column(name = "USER_ID")
private Long id;
#Column(name = "USER_NAME")
private String name;
#Column(name = "SALARY")
private String salary;
...getters and setters
}
And
#Entity
public class UserModel {
#Id
private Long id;
private String name;
private String salary;
private Integer totalRecords;
#Transient
private Integer rn;
...getters and setters
}
And I substituted these two classes with one like this:
public class NewUser {
private Long id;
private String name;
private String salary;
private Integer totalRecords;
private Integer rn;
...getters and setters
}
The table itself has only 3 fields: id, name and salary, the other 2 fields are created and filled later.
The repositiry the original Author has for the user looks like this:
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM MYUSERS", nativeQuery = true)
List<User> findAllByUsernames(List<String> listOfUsernames);
}
My own repository looks like this:
#Repository
public class NewUserRepoImpl extends JdbcDaoSupport implements NewUserRepo {
private static final String SELECT_ALL_SQL = "SELECT USER_ID as id, USER_NAME as name, SALARY as salary FROM MYUSERS";
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final JdbcTemplate jdbctemplate;
public NewUserRepoImpl(NamedParameterJdbcTemplate namedParameterJdbcTemplate, JdbcTemplate jdbctemplate, DataSource dataSource) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
this.jdbctemplate = jdbctemplate;
setDataSource(dataSource);
}
#Override
public List<NewUser> findAll(PaginationCriteria pagination) {
try {
String paginatedQuery = AppUtil.buildPaginatedQueryForOracle(SELECT_ALL_SQL, pagination);
return jdbctemplate.query(paginatedQuery, newUserRowMapper());
} catch (DataAccessException e) {
throw new EntityNotFoundException("No Entities Found");
}
}
#Bean
public RowMapper<NewUser> newUserRowMapper() {
return (rs, i) -> {
final NewUser newUser = new NewUser();
newUser.setId(rs.getLong("ID"));
newUser.setName(rs.getString("NAME"));
newUser.setSalary(rs.getString("SALARY"));
newUser.setTotalRecords(rs.getInt("TOTAL_RECORDS"));
newUser.setTotalRecords(rs.getInt("RN"));
return newUser;
};
}
}
the buildPaginatedQueryForOracle thing transforms my Query and allows it to get the totalRecords and rn. Below I will post the output of it both for the orifinal and my queries (they are the same, I checked).
So, the main part, the controller. I left the old and new pieces in it for now for debug purposes and just returning one of the results:
#RequestMapping(value="/users/paginated/orcl", method=RequestMethod.GET)
#ResponseBody
public String listUsersPaginatedForOracle(HttpServletRequest request, HttpServletResponse response, Model model) {
DataTableRequest<User> dataTableInRQ = new DataTableRequest<User>(request);
System.out.println(new Gson().toJson(dataTableInRQ));
DataTableRequest<NewUser> dataTableInRQNew = new DataTableRequest<NewUser>(request);
System.out.println(new Gson().toJson(dataTableInRQNew));
PaginationCriteria pagination = dataTableInRQ.getPaginationRequest();
System.out.println(new Gson().toJson(pagination));
PaginationCriteria paginationNew = dataTableInRQNew.getPaginationRequest();
System.out.println(new Gson().toJson(paginationNew));
String baseQuery = "SELECT USER_ID as id, USER_NAME as name, SALARY as salary FROM MYUSERS";
String paginatedQuery = AppUtil.buildPaginatedQueryForOracle(baseQuery, pagination);
String paginatedQueryNew = AppUtil.buildPaginatedQueryForOracle(baseQuery, paginationNew);
System.out.println(paginatedQuery);
System.out.println(paginatedQueryNew);
Query query = entityManager.createNativeQuery(paginatedQuery, UserModel.class);
System.out.println("Query:");
System.out.println(query);
#SuppressWarnings("unchecked")
List<UserModel> userList = query.getResultList();
System.out.println(new Gson().toJson(userList));
#SuppressWarnings("unchecked")
List<NewUser> userListNew = newUserRepo.findAll(paginationNew);
System.out.println(new Gson().toJson(userListNew));
DataTableResults<UserModel> dataTableResult = new DataTableResults<UserModel>();
DataTableResults<NewUser> dataTableResultNew = new DataTableResults<NewUser>();
dataTableResult.setDraw(dataTableInRQ.getDraw());
dataTableResultNew.setDraw(dataTableInRQNew.getDraw());
dataTableResult.setListOfDataObjects(userList);
dataTableResultNew.setListOfDataObjects(userListNew);
if (!AppUtil.isObjectEmpty(userList)) {
dataTableResult.setRecordsTotal(userList.get(0).getTotalRecords()
.toString());
if (dataTableInRQ.getPaginationRequest().isFilterByEmpty()) {
dataTableResult.setRecordsFiltered(userList.get(0).getTotalRecords()
.toString());
} else {
dataTableResult.setRecordsFiltered(Integer.toString(userList.size()));
}
}
if (!AppUtil.isObjectEmpty(userListNew)) {
dataTableResultNew.setRecordsTotal(userListNew.get(0).getTotalRecords()
.toString());
if (dataTableInRQ.getPaginationRequest().isFilterByEmpty()) {
dataTableResultNew.setRecordsFiltered(userListNew.get(0).getTotalRecords()
.toString());
} else {
dataTableResultNew.setRecordsFiltered(Integer.toString(userListNew.size()));
}
}
System.out.println(new Gson().toJson(dataTableResult));
System.out.println(new Gson().toJson(dataTableResultNew));
return new Gson().toJson(dataTableResult);
}
So, I log out everything possible in the console. Here is the output:
{"uniqueId":"1579786571491","draw":"1","start":0,"length":5,"search":"","regex":false,"columns":[{"index":0,"data":"id","name":"ID","searchable":true,"orderable":true,"search":"","regex":false,"sortDir":"ASC"},{"index":1,"data":"name","name":"Name","searchable":true,"orderable":true,"search":"","regex":false},{"index":2,"data":"salary","name":"Salary","searchable":true,"orderable":true,"search":"","regex":false}],"order":{"index":0,"data":"id","name":"ID","searchable":true,"orderable":true,"search":"","regex":false,"sortDir":"ASC"},"isGlobalSearch":false,"maxParamsToCheck":3}
{"uniqueId":"1579786571491","draw":"1","start":0,"length":5,"search":"","regex":false,"columns":[{"index":0,"data":"id","name":"ID","searchable":true,"orderable":true,"search":"","regex":false,"sortDir":"ASC"},{"index":1,"data":"name","name":"Name","searchable":true,"orderable":true,"search":"","regex":false},{"index":2,"data":"salary","name":"Salary","searchable":true,"orderable":true,"search":"","regex":false}],"order":{"index":0,"data":"id","name":"ID","searchable":true,"orderable":true,"search":"","regex":false,"sortDir":"ASC"},"isGlobalSearch":false,"maxParamsToCheck":3}
{"pageNumber":0,"pageSize":5,"sortBy":{"mapOfSorts":{"id":"ASC"}},"filterBy":{"mapOfFilters":{},"globalSearch":false}}
{"pageNumber":0,"pageSize":5,"sortBy":{"mapOfSorts":{"id":"ASC"}},"filterBy":{"mapOfFilters":{},"globalSearch":false}}
SELECT * FROM (SELECT FILTERED_ORDERED_RESULTS.*, COUNT(1) OVER() total_records, ROWNUM AS RN FROM (SELECT BASEINFO.* FROM ( SELECT USER_ID as id, USER_NAME as name, SALARY as salary FROM MYUSERS ) BASEINFO ) FILTERED_ORDERED_RESULTS ORDER BY id ASC ) WHERE RN > (0 * 5) AND RN <= (0 + 1) * 5
SELECT * FROM (SELECT FILTERED_ORDERED_RESULTS.*, COUNT(1) OVER() total_records, ROWNUM AS RN FROM (SELECT BASEINFO.* FROM ( SELECT USER_ID as id, USER_NAME as name, SALARY as salary FROM MYUSERS ) BASEINFO ) FILTERED_ORDERED_RESULTS ORDER BY id ASC ) WHERE RN > (0 * 5) AND RN <= (0 + 1) * 5
Query:
org.hibernate.query.internal.NativeQueryImpl#3ea49a4
[{"id":3,"name":"user3","salary":"300","totalRecords":18},{"id":4,"name":"user4","salary":"400","totalRecords":18},{"id":5,"name":"user5","salary":"500","totalRecords":18},{"id":6,"name":"user6","salary":"600","totalRecords":18},{"id":7,"name":"user7","salary":"700","totalRecords":18}]
[{"id":3,"name":"user3","salary":"300","totalRecords":1},{"id":4,"name":"user4","salary":"400","totalRecords":2},{"id":5,"name":"user5","salary":"500","totalRecords":3},{"id":6,"name":"user6","salary":"600","totalRecords":4},{"id":7,"name":"user7","salary":"700","totalRecords":5}]
{"draw":"1","recordsFiltered":"18","recordsTotal":"18","data":[{"id":3,"name":"user3","salary":"300","totalRecords":18},{"id":4,"name":"user4","salary":"400","totalRecords":18},{"id":5,"name":"user5","salary":"500","totalRecords":18},{"id":6,"name":"user6","salary":"600","totalRecords":18},{"id":7,"name":"user7","salary":"700","totalRecords":18}]}
{"draw":"1","recordsFiltered":"1","recordsTotal":"1","data":[{"id":3,"name":"user3","salary":"300","totalRecords":1},{"id":4,"name":"user4","salary":"400","totalRecords":2},{"id":5,"name":"user5","salary":"500","totalRecords":3},{"id":6,"name":"user6","salary":"600","totalRecords":4},{"id":7,"name":"user7","salary":"700","totalRecords":5}]}
It helped me realize that:
DataTableRequest incoming from the back is the same for both jpa
and jdbc
PaginationCriteria are also the same
paginatedQuery
having been made with the method specified above are the same.
Differences are already seen in the Lists: where the Jpa list
retrieved with native Query has totalRecords as 18 for every row,
the JDBC repo with the same query returns 1,2,3... for every
subsequent row.
It made me think that I should look at the Query made for JPA. But, as you see in the log, System.out.println wasn't able to decipher it for some reason.
Any advice on how to decipher it and more importantly how to get the right total result for each row would be greatly appreciated!!!
How can I execute Multiple SQL statements in a single sql query using hibernate native sql.
String sql = "SELECT * FROM user; SELECT * FROM product;";
UserVO valueObject = new UserVO();
databaseObject.select(sql, valueObject);
Database Object
public List select(String sql, Object valueObject) throws Exception {
Session session = Entitlement.getSessionFactory().openSession();
session.beginTransaction();
List list = session.createSQLQuery(sql).setProperties(valueObject).list();
session.close();
return list;
}
Use union to form a query which has same returning data
(Select EMPLOYEEID as id, EMPLOYEE_NAME as name, "EMPOYEE" as type) UNION (SELECT PRODUCTID as id, Product_NAME as name, "PRODUCT" as type)
form an Entity to hold it
class EntityDetail {
String id;
String name;
String type;
}
I have added an additional column value type to simply identify from which table row is coming from. And yes you will need to form a proper Entity with all valid annotations the above Entity is just for example.
just a lateral approach.
public List<List<Object[]>> execute(String sqls, Object valueObject) throws Exception {
String[] queries = sqls.split(";");
List<List<Object[]>> result = new ArrayList<>();
for(int i=0; i<queries.length; i++) {
result.add(this.select(queries[i], valueObject));
}
return result;
}
I'm trying to execute a simple stored procedure with Spring/Hibernate using Annotations.
Here are my code snippets:
DAO class:
public class UserDAO extends HibernateDaoSupport {
public List selectUsers(final String eid){
return (List) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws
HibernateException, SQLException
{
Query q = session.getNamedQuery("SP_APPL_USER");
System.out.println(q);
q.setString("eid", eid);
return q.list();
}
});
}
}
my entity class:
#Entity
#Table(name = "APPL_USER")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula(value = "SUBSCRIBER_IND")
#DiscriminatorValue("N")
#NamedQuery(name = "req.all", query = "select n from Requestor n")
#org.hibernate.annotations.NamedNativeQuery(name = "SP_APPL_USER",
query = "call SP_APPL_USER(?, :eid)", callable = true, readOnly = true, resultClass = Requestor.class)
public class Requestor {
#Id
#Column(name = "EMPL_ID")
public String getEmpid() {
return empid;
}
public void setEmpid(String empid) {
this.empid = empid;
}
#Column(name = "EMPL_FRST_NM")
public String getFirstname() {
return firstname;
}
...
}
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
APFUser user = (APFUser)ctx.getBean("apfUser");
List selectUsers = user.getUserDAO().selectUsers("EMP456");
System.out.println(selectUsers);
}
}
and the stored procedure:
create or replace PROCEDURE SP_APPL_USER (p_cursor out sys_refcursor, eid in varchar2)
as
empId varchar2(8);
fname varchar2(50);
lname varchar2(50);
begin
empId := null;
fname := null;
lname := null;
open p_cursor for
select l.EMPL_ID, l.EMPL_FRST_NM, l.EMPL_LST_NM
into empId, fname, lname
from APPL_USER l
where l.EMPL_ID = eid;
end;
If i enter invalid EID, its returning empty list which is OK.
But when record is there, following exception is thrown:
Exception in thread "main" org.springframework.jdbc.BadSqlGrammarException: Hibernate operation: could not execute query; bad SQL grammar [call SP_APPL_USER(?, ?)]; nested exception is java.sql.SQLException: Invalid column name
Do I need to modify the entity(Requestor.class) ?
How will the REFCURSOR be converted to the List?
The stored procedure is expected to return more than one record.
That's because of the bug in the hibernate.
I've modified the stored procedure to fetch all the columns and it worked well.