Unable to map JSON field from postgres DB to POJO class - java

I have below code in place where I am trying to get only 1 column (JSON type) from postgres DB and map it to a POJO class. But, in the result I am getting correct count but with null values only, even though data is present. Any Help/Suggestion is appreciated.
POJO Class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class EmployeePayload {
private String empName;
private String empID;
//getters and setters
}
Query Execution Code ::
String query = "SELECT emp_payload FROM EmpDetails";
ResultSetHandler<List<EmployeePayload>> employeePayload = new BeanListHandler<EmployeePayload>(EmployeePayload.class);
List<EmployeePayload> resultList = runner.query(dbConnection, query, employeePayload);
log.info(":: resultList is :: " + resultList);
Data in DB ::
"{""empName"":""james"",""empID"":""008""}",
"{""empName"":""bond"",""empID"":""007""}"
Result in Log ::
resultList is :: [EmployeePayload{empName='null', empID='null'}, EmployeePayload{empName='null', empID='null'}]

The BeanListHandler comes from apache dbutils (docs)
By looking at the docs / source, it's implemented by mapping single columns to single properties of the Bean. You are essentially trying to map a single column to multiple properties which does not work. Now there are two ways to go about this:
Writing your own RowProcessor
Rewriting the query to return multiple columns.
In this situation I would favor the second solution for it's simplicity as postgres has this functionality built-in for its json field types.
Your query could look like this:
String query = "SELECT emp_payload->'empName' AS empName, emp_payload->'empID' AS empID FROM EmpDetails";
(The AS ... might not be necessary but I don't know how psql generates the column names for extracted json values).
If you would execute this query directly in postgres, you would get a result set with column names empName and empID which is exactly what the BeanProcessor (the default processor for BeanListHandler) expects.

Related

Storing the keysets from a JPQL query result in a java list

I was successfully able to execute a jpql query and print the result which is stored in a queryResults variable. What I want to achieve next is storing just the IDs (primary key column) in a list without the date (value), but I am not too sure if this is possible; perhaps using something like a java map. Is it possible? If yes, how can this be easily achieved?
private static final TestDao Test_DAO = new TestDao();
#Test
public void testById() {
List<TestEntity> queryResults = TEST_DAO.findById(""); //The record from the sql query is stored in queryResults and findById("") is the method that executes the query in a TestDao class and it is called here
for (TestEntity qResult: queryResults) { // looping through the query result to print the rows
System.out.println(qResult.getId());
System.out.println(qResult.getDate());
}
System.out.println("This is the sql result " + queryResults );
}
Output:
This is the result [TestEntity(id=101, date=2020-01-19 15:12:32.447), TestEntity(id=102, date=2020-09-01 11:04:10.0)]// I want to get the IDs 101 and 102 and store in a list without the Dates
I tried using a map this way:
Map<Integer, Timestamp> map= (Map<Integer, Timestamp>) queryResults.get(0); but I got an exception:
java.lang.ClassCastException: TestEntity cannot be cast to java.util.Map
There are some points before the implementation.
Why are you defining DAO as static? I think this is a bad implementation unless I am missing a particular reason you declared it static. You should define this as a member variable and not a static member
The naming of the method - findById() translated in English is - find Something by this Id, but you are fetching a list of Records, so naming is not correct.
Point 2 becomes invalid if ID property is not a Primary Key in your table, then it makes sense, but still naming is bad. Id is something we use to define Primary Key in the Database and should be and will be unique. But your comments suggest that ID is unique and the Primary Key. So read about how Databases work
And even if not unique, if you pass an Id to find some records, why will get different ids in the Records !!!
About implementation:
Changing in your existing code:
private TestDao Test_DAO = new TestDao();
#Test
public void testById() {
List<TestEntity> queryResults = TEST_DAO.findById("");
List<Long> listOfIds = new ArrayList<>(); // Assuming Id is Long type, same logic for any type
for (TestEntity qResult: queryResults) {
System.out.println(qResult.getId());
listOfIds.add(qResult.getId()); // Just add it to the list
System.out.println(qResult.getDate());
}
}
In case you want to be efficient with the query:
You can use JPQL and hibernate
You can then write a query like:
String query = "select te.id from TestEntity te";
// Create the TypedQuery using EntityManager and then get ResultSet back
List<Long> ids = query.getResultList();
In case of using Spring-Data-Jpa, you can define the repository and define the method and pass the query with #Query annotation. Spring Data JPA

How to set columns from a joined entity in JPAQuery and Querydsl

I am in the process of internationalizing an existing software using. As part of it I have a table which, reduced to the simplest case, is:
ID AUTONUMERIC
ID_OF_TEXT NUMBER
TEXT VARCHAR
The text column must be translated to new languages (which may or may not be present). So there is another new table with colunmns:
ID_OF_TEXT NUMBER
LANGUAGE_CODE VARCHAR
TRANSLATED_TEXT VARCHAR
There is already an entity to represent the base table and this entity cannot be changed. It's generated code looks like:
class QMyBaseEntity extends EntityPathBase<MyBaseEntity> {
NumberPath<Long> id = createNumber("id", Long.class);
NumberPath<Long> toTranslateId = createNumber("toTranslateId", Long.class);
StringPath text = createString("text");
}
and the generated code for the translations look up table looks like:
#Generated("com.mysema.query.codegen.EntitySerializer")
class QTranslationLookup extends EntityPathBase<TranslationLookup> {
NumberPath<Long> id = createNumber("id", Long.class);
NumberPath<Long> translationId = createNumber("translationId", Long.class);
StringPath languageCode = createString("languageCode");
StringPath translated_text = createString("translated_text");
}
I want the field text of MyBaseEntity to take the value of the translation table (if it exists) instead of its original one. There is already a very complex query that I cannot change except for adding a join like:
String languageCode = "de";
JPAQuery query = new JPAQuery(entityManager);
query.from(qMyBaseEntity);
// add lots of other joins and stuff here
query.leftJoin(qTranslationLookup)
.on(qTranslationLookup.translationId.eq(qMyBaseEntity.toTranslateId)
.and(qTranslationLookup.languageCode.eq(languageCode)));
List<MyBaseEntity> results = query.list(qMyBaseEntity);
With the minimum change possible to the existing code, how do I set the value of the translation in the results list so that the returned entity instances contain the translation in the text column instead of the original value from the old (untranslated) table?
Mysema isn't a framework, QueryDSL is. QueryDSL used to be developed by Mysema, but isn't anymore and the package has since moved to the com.querydsl group id.
You can't override the values of managed properties in an entity projection. The values will always be the actual field values from the entity. Entities cannot be used as DTO's. If you want to project different kinds of expressions, you need to use tuple projection.
List<Tuple> results = query.from(qMyBaseEntity)
.leftJoin(qTranslationLookup)
.on(qTranslationLookup.translationId.eq(qMyBaseEntity.toTranslateId)
.and(qTranslationLookup.languageCode.eq(languageCode)))
.select(qMyBaseEntity, qTranslationLookup.translatedText.coalesce(qMyBaseEntity.text))
.fetch()
Alternatively, you could for example return a mapping:
Map<MyBaseEntity, String> results = query.from(qMyBaseEntity)
.leftJoin(qTranslationLookup)
.on(qTranslationLookup.translationId.eq(qMyBaseEntity.toTranslateId)
.and(qTranslationLookup.languageCode.eq(languageCode)))
.transform(GroupBy.groupBy(qMyBaseEntity).as(
qTranslationLookup.translatedText.coalesce(qMyBaseEntity.text))
.fetch()
Or you could use QueryDSL's DTO projection:
List<ResultDTO> results = query.from(qMyBaseEntity)
.leftJoin(qTranslationLookup)
.on(qTranslationLookup.translationId.eq(qMyBaseEntity.toTranslateId)
.and(qTranslationLookup.languageCode.eq(languageCode)))
.select(Projections.constructor(ResultDTO.class, qMyBaseEntity, qTranslationLookup.translatedText.coalesce(qMyBaseEntity.text))
.fetch()

Hibernate - Query columns map into object, the rest of the columns set to null or default value

Basically if we want to query specific columns, we do this:
Query query =
session.createQuery("SELECT tr.review from TravelReview as tr");
List<String> reviews = query.list();
And if we want the original object (TravelReview) instead of List of String, we do this:
String QUERY = "SELECT new City(tr.title, tr.review ) from TravelReview as tr";
List<City> cities = session.createQuery(QUERY).list();
I found it very troublesome to purposely create a Java constructor for the purpose above. (It will end up I have many constructors for this.)
Is there a way to map the selected columns automatically and return the original object (TravelReview), only the attributes that match the selected columns have values, the rest of the attributes will be null (or default value)? Basically something like Spring JdbcTemplate BeanPropertyRowMapper which is very useful and convenient.

Morphia to return List of strings which are the fields of all documents

If I got a collection full of following elements
#Entity
public void MyEntity{
public String name;
public String type;
...
}
And I want to return a List<String> (or Set) of not the elements, but only their name fields.
List<String> allNames = datasotre.find(MyEntity.class).asList("name");
This is sample query, there is no such method of Morphia datastore.
To limit the fields returned call the "retrievedFields" method on Query. For example, to only get the name field of all MyEntity objects:
datastore.find(MyEntity.class).retrievedFields( true, "name").asList()
Edit - You can get a list Strings using the following query as long as you don't mind that the list will only contain unique values (i.e. no duplicate names):
DBCollection m = datastore.getCollection( MyEntity.class );
List names = m.distinct( "name", new BasicDBObject() );
The "names" list will only contain Strings.
The problem here is that there is no actual query for "keys". The queries all return "key/value pairs".
In theory, the fields in datastore.find() should map to the fields in MyEntity so you could just use reflection. However, if you have other people writing to the DB from different places they may have seeded extra tables.
If this is the case, you will need to run a Map/Reduce to get the list of all the "key" names.
There is a sample here.
You are looking for datastore.find(MyEntity.class).retrievedFields(true, "name").asList();. This will contain the _id and name attribute.
See http://code.google.com/p/morphia/wiki/Query#Ignoring_Fields

How to best map results from an SQL query to a non-entity Java object using Hibernate?

I have a Hibernate managed Java entity called X and a native SQL function (myfunc) that I call from a Hibernate SQL query along these lines:
SQLQuery q = hibernateSession.createSQLQuery(
"SELECT *, myfunc(:param) as result from X_table_name"
);
What I want to do is to map the everything returned from this query to a class (not necessarily managed by Hibernate) called Y. Y should contain all properties/fields from X plus the result returned by myfunc, e.g. Y could extend class X and add a "result" field.
What I've tried:
I've tried using q.addEntity(Y.class) but this fails with:
org.hibernate.MappingException: Unknown entity com.mycompany.Y
q.setResultTransformer(Transformers.aliasToBean(Y.class)); but this fails with: org.hibernate.PropertyNotFoundException: Could not find setter for some_property. X has a field called someProperty with the appropriate getter and setter but in this case it doesn't seem like Hibernate maps the column name (some_property) to the correct field name.
q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); returns a Map but the values are not always of the type expected by the corresponding field in X. For example fields in X of type enum and Date cannot be mapped directly from the Map returned by the SQL query (where they are Strings).
What's the appropriate way to deal with this situation?
See the chapter of the documentation about SQL queries.
You can use the addScalar() method to specify which type Hibernat should use for a given column.
And you can use aliases to map the results with the bean properties:
select t.some_property as someProperty, ..., myfunc(:param) as result from X_table_name t
Or, (and although it require some lines of code, it's my preferred solution), you can simply do the mapping yourself:
List<Object[]> rows = query.list();
for (Object[] row : rows) {
Foo foo = new Foo((Long) row[0], (String) row[1], ...);
}
This avoids reflection, and lets you control everything.
Easy. Cast the rows to Map<String, Object>:
final org.hibernate.Query q = session.createSQLQuery(sql);
q.setParameter("geo", geo);
q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
final List<Map<String, Object>> src = q.list();
final List<VideoEntry> results = new ArrayList<VideoEntry>(src.size());
for (final Map<String, Object> map:src) {
final VideoEntry entry = new VideoEntry();
BeanUtils.populate(entry, map);
results.add(entry);
}
First of all you need to declare the entity in the hibernate configuration xml file something like this: .....
class="path to your entity"
Or you can do the same thing programatically before you make the query.

Categories