Eclipselink jpa - sql query result mapping to java object - java

I am trying to return a typed list from getResultList() but I am having issues with mapping my sql result list to a typed list. It keeps returning a list of generic objects. This is my current code:
EntityManager em = this.emPool.createEntityManager();
TypedQuery<Runtime> query = Runner.getRuntime(em);
List<Runtime> runtimeList = query.getResultList();
Also, in Runner class I have this:
public static TypedQuery<Runtime> getRuntime(EntityManager em) {
return em.createNamedQuery(COUNT_RUNTIMES_SQL_EXPRESSION, Runtime.class);
}
And here is the query:
SELECT u.runner_id as runnerId, COUNT(u.times) FROM RUNNER u"
+ " WHERE u.age = :60 GROUP by u.runner_id
Any tips will be greatly appreciated.
NOTE: the query I am running is a report query -> a simple group by and count

You need to use constructor expression in select clause. For example,
SELECT new com.example.Runtime(u.runner_id as runnerId, COUNT(u.times)) FROM RUNNER u"
+ " WHERE u.age = :60 GROUP by u.runner_id
Define the corresponding constructor in the class Runtime.
If using Criteria API, CriteriaBuilder.construt(...) should be used. Tested with Cmobilecom JPA for the criteria API.
Disclaimer: I am a developer of Cmobilecom JPA (for java and android)

Related

JPA calling function in a native query is sending multiple parameters instead a UUID Collection

I created a PostgreSQL function to get some data from an array of UUID.
i.e:
create function journey_statistics(journey_ids uuid[])
returns TABLE(project_id uuid, project_name character varying,...)
language plpgsql
If I run the next sql statement it returns the expected data:
select * from journey_statistics(array['0f36c7a5-04eb-4329-8e93-a13625a4ffa6'::uuid, 'bc10ee72-7b7f-4bbd-a70a-75477b484d58'::uuid])
But then, when I implement it on Java and run it. I am getting the next error:
o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: function journey_statistics(uuid, uuid) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
This is the native query I am using to call it. And I have used same in other similar functions with no errors. So I can not understand what is the issue or what I am doing wrong.
#Query(value = "select cast(project_id as varchar(36)) as projectId, project_name as projectName, cast(project_leader as varchar(36)) as projectLeader" +
" from journey_statistics(:uuids)", nativeQuery = true)
Collection<JourneyStatisticsView> getJourneyStatisticsById(Collection<UUID> uuids);
I have tried to cast data to an array but it looks it is transform to a record[] array.
But more strange is if I pass a Collection of Strings and then I try to cast them I get
function journey_statistics(character varying, character varying) does not exist
Any help appreciated, thank you.
I found a workaround to pass the Collection into the function.
It is not as sophisticated as I wanted. But at least it works.
Basically, I have created a new Repository to use the entity manager and create my own sql statement.
#Repository
public class JourneyStatisticsCustomRepositoryImpl implements JourneyStatisticCustomRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List getJourneyStatisticsByIds(Collection<UUID> uuids) {
final Collection<String> formattedUUID = uuids.stream().map(uuid -> "cast('" + uuid + "' as uuid)").collect(Collectors.toSet());
final String joinedUUIDs = Strings.join(formattedUUID.iterator(), ',');
return entityManager.createNativeQuery("select cast(project_id as varchar(36)) as projectId, ..." +
"... from journey_statistics(array[" + joinedUUIDs +"])", JourneyStatisticsView.class).getResultList();
}
This sends the proper collection to the function and returns the data as expected.
Hope this could help someone else with similar issues.

Stored procedure call with Spring JPA

I need to invoke a stored procedure using the JPA. The stored procedure operates on multiple tables and return some of the columns from these tables.
Tried with the #Procedure it doesn't seem to work, always the stored procedure is not found in this case.
Directly calling the procedure using native query was successful, but with this approach, I am need to map the result returned to List of an Object.
My implementation in the repository looks like below,
#Query(value = "EXECUTE dbs.multitable_Test :inputObj", nativeQuery = true)
List<sp> multitable_Test(#Param("inputObj")String inputObj);
The result returned from the stored procedure needs to be mapped to the sp class.
How can this be achieved while we have multiple tables response in the single result set?
Already tried with the attributeConvert from
this link, still getting the below exception.
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type
Any help with this is appreciated.
Firstly, this is not really the use case for procedure. Procedure is meant to modify data on the database without any return value, then you could use:
#Procedure(procedureName = "procedure_name")
void procedure(); // notice void
You should rather use a function using create function syntax. Function can modify data and return the result.
Secondly if you want to map it to some class, I see two solutions (using EntityManager):
Using ResultTransformer:
entityManager.createNativeQuery(
"select * from function_name(:parameter)"
)
.setParameter("parameter", parameter)
.unwrap(org.hibernate.query.NativeQuery.class)
.setResultTransformer(new ResultTransformer() {
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
return new Sp(tuple[0]);
}
#Override
public List transformList(List collection) {
return collection;
}
})
.getResultList();
Note that ResultTransformer is deprecated, but is so powerful, it will not be removed until there is a sensible replacement, see the note from hibernate developer.
Using ResultSetMapping. Place the proper annotation over an entity:
#SqlResultSetMapping(
name = "sp_mapping",
classes = #ConstructorResult(
targetClass = Sp.class,
columns = {
#ColumnResult(name = "attribute", type = Long.class)
})
)
And invoke the function using the mapping as parameter:
entityManager.createNativeQuery(
"select * " +
"from function_name(:parameter);",
"sp_mapping"
)
.setParameter("parameter", parameter)
.getResultList();

How to pass query param to REST API to do a IN query in DB [duplicate]

This question already has answers here:
PreparedStatement IN clause alternatives?
(33 answers)
Closed 5 years ago.
Say that I have a query of the form
SELECT * FROM MYTABLE WHERE MYCOL in (?)
And I want to parameterize the arguments to in.
Is there a straightforward way to do this in Java with JDBC, in a way that could work on multiple databases without modifying the SQL itself?
The closest question I've found had to do with C#, I'm wondering if there is something different for Java/JDBC.
There's indeed no straightforward way to do this in JDBC. Some JDBC drivers seem to support PreparedStatement#setArray() on the IN clause. I am only not sure which ones that are.
You could just use a helper method with String#join() and Collections#nCopies() to generate the placeholders for IN clause and another helper method to set all the values in a loop with PreparedStatement#setObject().
public static String preparePlaceHolders(int length) {
return String.join(",", Collections.nCopies(length, "?"));
}
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
Here's how you could use it:
private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)";
public List<Entity> find(Set<Long> ids) throws SQLException {
List<Entity> entities = new ArrayList<Entity>();
String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size()));
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
) {
setValues(statement, ids.toArray());
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
entities.add(map(resultSet));
}
}
}
return entities;
}
private static Entity map(ResultSet resultSet) throws SQLException {
Enitity entity = new Entity();
entity.setId(resultSet.getLong("id"));
entity.setName(resultSet.getString("name"));
entity.setValue(resultSet.getInt("value"));
return entity;
}
Note that some databases have a limit of allowable amount of values in the IN clause. Oracle for example has this limit on 1000 items.
Since nobody answer the case for a large IN clause (more than 100) I'll throw my solution to this problem which works nicely for JDBC. In short I replace the IN with a INNER JOIN on a tmp table.
What I do is make what I call a batch ids table and depending on the RDBMS I may make that a tmp table or in memory table.
The table has two columns. One column with the id from the IN Clause and another column with a batch id that I generate on the fly.
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
Before you select you shove your ids into the table with a given batch id.
Then you just replace your original queries IN clause with a INNER JOIN matching on your ids table WHERE batch_id equals your current batch. After your done your delete the entries for you batch.
The standard way to do this is (if you are using Spring JDBC) is to use the org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate class.
Using this class, it is possible to define a List as your SQL parameter and use the NamedParameterJdbcTemplate to replace a named parameter. For example:
public List<MyObject> getDatabaseObjects(List<String> params) {
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
String sql = "select * from my_table where my_col in (:params)";
List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
return result;
}
I solved this by constructing the SQL string with as many ? as I have values to look for.
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
First I searched for an array type I can pass into the statement, but all JDBC array types are vendor specific. So I stayed with the multiple ?.
I got the answer from docs.spring(19.7.3)
The SQL standard allows for selecting rows based on an expression that includes a variable list of values. A typical example would be select * from T_ACTOR where id in (1, 2, 3). This variable list is not directly supported for prepared statements by the JDBC standard; you cannot declare a variable number of placeholders. You need a number of variations with the desired number of placeholders prepared, or you need to generate the SQL string dynamically once you know how many placeholders are required. The named parameter support provided in the NamedParameterJdbcTemplate and JdbcTemplate takes the latter approach. Pass in the values as a java.util.List of primitive objects. This list will be used to insert the required placeholders and pass in the values during the statement execution.
Hope this can help you.
AFAIK, there is no standard support in JDBC for handling Collections as parameters. It would be great if you could just pass in a List and that would be expanded.
Spring's JDBC access supports passing collections as parameters. You could look at how this is done for inspiration on coding this securely.
See Auto-expanding collections as JDBC parameters
(The article first discusses Hibernate, then goes on to discuss JDBC.)
See my trial and It success,It is said that the list size has potential limitation.
List l = Arrays.asList(new Integer[]{12496,12497,12498,12499});
Map param = Collections.singletonMap("goodsid",l);
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);
There are different alternative approaches that we can use.
Execute Single Queries - slow and not recommended
Using Stored Procedure - database specific
Creating PreparedStatement Query dynamically - good performance but loose benefits of caching and needs recompilation
Using NULL in PreparedStatement Query - I think this is a good approach with optimal performance.
Check more details about these here.
sormula makes this simple (see Example 4):
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
for (Inventory inventory: inventoryTable.
selectAllWhere("partNumberIn", partNumbers))
{
System.out.println(inventory.getPartNumber());
}
One way i can think of is to use the java.sql.PreparedStatement and a bit of jury rigging
PreparedStatement preparedStmt = conn.prepareStatement("SELECT * FROM MYTABLE WHERE MYCOL in (?)");
... and then ...
preparedStmt.setString(1, [your stringged params]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

Getting empty result from the #Query in Hibernate

I am using Hibernate to execute my query, in admin panel i am getting correct result but while using in Hibernate it is not giving any result.
Dao layer -
#Query("select new com.eventila.pms.entity.ReferenceLead(projectId,count(lm)) from LeadMaster lm where lm.vendorId= ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Pojo -
#lombok.Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReferenceLead {
String projectId;
Long referenceLead;
Long count;
protected ReferenceLead(){}
public ReferenceLead(String projectId,Long count) {
this.projectId=projectId;
this.count=count;
}
}
After executing this i am getting a empty list.
Please help me out.
In your select query return the fields without calling new constructor:
#Query("select projectId, count(lm) from LeadMaster lm where lm.vendorId = ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Hibernate will instantiate the object using these fields. Also, add #Entity annotation to your ReferenceLead class.
'source' is the keyword in SQL.
It is a keyword used in MERGE. i.e. WHEN NOT MATCHED BY SOURCE.
The word MATCHED also exhibits the same behaviour in that it gets highlighted grey in the editor.
Neither of these are reserved keywords though so if used as an identifier they do not need to be delimited (unless you find the syntax highlighting distracting).

can hibernate hsql automatically map the query result to a class?

I wrote a hsql:
String queryString = "select t1.a, t1.b, t2.c from table1 t1, table2 t2 where t1.id = t2.id";
and then I have a class:
class test{
String a;
String b;
String c
....//other getter and setter
}
I tried:
List = getHibernateTemplate().find(queryString);
this doesn't work, when I use test object in jsp page, it will throw out exception.
I have to manually create a test object:
List<Object[]> list = getHibernateTemplate().find(queryString);
test.seta(list.get(0)[0]);
is it possible for hibernate to automatically map the class for me in hsql ?
If you have a mapping for both table1 and table2 (see Prashant question above) you can do something like:
String queryString = "select t1 from table1 t1
inner join t1.table2 t2";
After you run the query you should have a list of t1 objects.
for(Table1 t1:listOfTable1Objects) {
t1.getA(); //for example or whatever you want to do with your object.
}
The Problem is that you do not write a HQL query. You just write a normal SQL query. In HQL, because the hibernate make the mapping from table to class, you cannot make a projection. So, if you write something like
String query = "FROM Class1 WHERE ome_condition;
without the SELECT clause, the Hibernate will be able to convert the result in the proper object.
You can see more about this here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html
If you dont have a mapping, you may create a auxiliary class for this. Say ResultClass. Then you add #NamedNativeQuery and #SqlResultSetMapping annotations to the class:
#NamedNativeQuery(name="queryHehehe", query="select t1.field1 f1, t2.field2 f2 from table1 t1, table2 t2", resultSetMapping="mappingHehehe")
#SqlResultSetMapping(name="mappingHehehe", entities={
#EntityResult(entityClass=my.clazz.AuxiliaryClass.class, fields = {
#FieldResult(name="id", column="f1"),
#FieldResult(name="other_property", column="f2")
}),
})
public class AuxiliaryClass {
public Long id;
public String other_property;
}
I have never used this, but can work. Good luck.
If you need a query to return values from multiple tables and create an object of an unmapped class, then you need to either do what you're doing here, or use a ResultTransformer.
In order to do this with HibernateTemplate, you'll need to change the way you use the template, possibly using execute(HibernateCallback action), as you'll need to convert the sql query to a Criteria as described in Hibernate Reference Native SQL Chapter.
If you do want to try this, you'll probably want to use an AliasToBeanResultTransformer or AliasToBeanConstructorResultTransformer rather than writing your own transformer.

Categories