javaee 6 rest api named query result - jpa

I have a simple JEE6 rest class that gets the data from db2. I am using Jackson in ApplicationConfig class to convert the entity objects to json. It converts with the field names as the key and the value as the right hand value. So for example:
Class Entity {
String name;
String address;
}
converts to
{name:"hello", address:"world"}
The service is as follows:
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
Now I want to only return the name in json format. So I created a named query as follows in the entity class:
#NamedQuery(name = "justGetName", query = "SELECT a.name FROM Applications a")
And the service changed to
public List<T> findAll() {
return getEntityManager().createNamedQuery("justGetName").getResultList();
}
This returns the following array:
[{"first","second","third"}]
But I want to get back:
[{name:"first",name:"second",name:"third"}]
How do I write the named query so that the class field names are added to the json structure? Thank you.

You querying a list of strings from your database and this is what the service returns.
Their are multiple ways to achieve your goal.
Pure JPA
Using #JsonIgnore to tell Jackson not to serialize an attribute
class Application {
String name;
#JsonIgnore
String address;
}
Create a new Entity class that only contains the attributes you would like to share
class ApplicationName {
String name;
}
Alternatively you could introduce a separate class that only contains the attributes you would like to share and convert the results from the query into this class and return than the list of this converted values.

Related

JPA query attribute with a converter

I have a SpringBoot 2.6.11 application with JPA 2.2.
I have an entity like this:
#Data
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
....
#Convert(converter = ListConverter.class)
private List<String> referenceCode;
....
}
I have this Converter:
#Converter(autoApply = true)
public class ListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(";", attribute);
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
return new ArrayList<>(Arrays.asList(dbData.split(";")));
}
}
And when I insert or extract this element all working fine. But now I wanna query that element and I don't know how to do it. If I do something like that:
public List<Entity> findByReferenceCode(String reference);
It doesn't work, if I do:
#Query("select e from Entity e where e.referenceCode IN ?1")
public List<Entity> findByReferenceCode(List<String> reference);
Still doesn't work..
The only way I found is by the nativeQuery but is really an extrema ratio. Ho can I solve this?
Thank you
To really do what you want here, you need to use an #ElementCollection. The reason being that there is no reliable way for JPA to query a single column and treat it as a collection. Reliably querying a collection requires a second table (which is what #ElementCollection does). You can continue to use the #Converter, but your queries will have to be customized to handle the disparity between the entity attribute type (list) and the actual database column type (string).
If you are okay with the limitations of the #Converter then it's fine (I have used them this way) but if you truly need to query the attribute like a collection (e.g. search for multiple independent items, perform counts, aggregations, etc) and you want those queries to be generated by a JPA layer, then you will have to use #ElementCollection and let it create a second table.

EAGER loading with one select doesn't work in Spring Data JPA

there is already a similar post. Since this is already older, I hope something has changed since then (How does the FetchMode work in Spring Data JPA)
I would like to run all jpa repository#findById in one select, if the relationship is marked with EAGER. However, spring data ignores the EAGER specification and the FETCH.JOIN annotation from hibernate.
Is there a generic solution that all findById queries are executed in one select?
I wouldn't want to write a separate JPL or EntityGraph for each query. Does anyone know a generic solution?
JpaReposistory
The easiest option would be to write a JpaRepository<T, Id>. This is still a custom repository. However, you do not have to write so much code. You mainly have to write a repository interface for each relevant class and annotate the findById(Long id) method with a graph. The advantage is that if you edit your entity, the repository method will not need any changes because you define the entity graph within the entity class itself.
#Entity
#NamedEntityGraph(name = "Department.detail",
attributeNodes = #NamedAttributeNode("employees"))
public class Department {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
// ...
}
public interface DepartmentRepository extends JpaRepository<Department, Long> {
#EntityGraph(value = "Department.detail", type = EntityGraphType.LOAD)
List<Department> findById(Long id);
}
As Spring data ignores the #Fetch(Fetchmode.JOIN) annotation or the information fetch = FetchType.EAGER, you cannot influence the join how you want it to be within the entity itself.
JPQL Query Where You Need It
Another option can be considered as a bad software engineering style: You can call the database queries directly where you need them. This means that you execute the code which you would usually write in the repository.
public ClassWithQueryResults {
#PersistenceContext
private EntityManager entityManager;
public void methodWhereYouNeedYourResults() {
TypedQuery<Department> query = entityManager.createQuery(
"SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e",
Department.class);
List<Department> departments = query.getResultList();
// ...
}
}
Repository With JPQL, Generics and Reflection
Taking the previously suggested idea, you can create a custom repository which is valid for all your entities. The first step would be to create an attribute in your entity class in which you store the attribute which should be fetched.
public class Department extends AbstractEntity {
public static void String ATTRIBUTE_TO_FETCH = "employees";
...
}
With some tweaking, this can be extended to an array/list of all the fields which should be fetched. As this attribute is directly in your entity classes, the chance for any mistakes and future effort is low. Obviously, this attribute should have the same name in all your entities.
The next step would be to create the repository. I provide an example with the findAll() method. You have to pass it only the class name of the entities you want to have and the generics and reflection do the rest. (Consider what you want to do with the exceptions.)
public <T> List<T> findAll(Class<T> tClass)
throws NoSuchFieldException, IllegalAccessException {
String className = tClass.getSimpleName();
String attributeToFetch = (String)
tClass.getDeclaredField("ATTRIBUTE_TO_FETCH").get(null);
String queryString = String.format("SELECT DISTINCT p FROM %s p LEFT JOIN p.%s c",
className, attributeToFetch);
TypedQuery<T> query = entityManager.createQuery(queryString, tClass);
return query.getResultList();
}
Depending on how you want to implement this, the modification/generation of a query through simple manipulation of a String can offer the possibility of SQL injection attacks.

How can I store a Java HashMap<String,Double> in mongodb using Morphia?

I have a user defined class CostMatrix this class contains two properties and data type for both properties is HashMap. I am using Morphia to communicate with mongodb so my entity class looks like this
#Entity(value = "CostMatrix",noClassnameStored = false)
public class CostMatrix {
#Id
private String id;
private HashMap<String,Double> distances;
private HashMap<String,Double> durations;
public CostMatrix(){}
public CostMatrix(String id, HashMap<String,Double>distances, HashMap<String,Double>durations) {
this.id = id;
this.distances = distances;
this.durations = durations;
}
I am unable to store object properly into database object is stored any how but when I retrieve its just returns id and class name any thoughts would be appreciated.
if you don't want to have any class/package names in your collection just put the noClassnameStored flag to true.
#Entity(value = "CostMatrix",noClassnameStored =**true**)
As for the saving part, do you fill some values into your maps? The mapper will ignore null values and empty lists.

(JDBI/Dropwizard) PSQLException when retrieving auto-incremented id from PostgreSQL

I'm trying to set up a dropwizard project but I'm stuck. When I try to get the auto generated id field with #GetGeneratedKeys then I'm getting the following Exception:
org.postgresql.util.PSQLException: Bad value for type long : foo.
The request is a simple JSON Request
{"name":"foo"}
The INSERT into the database is successful but it seems that the statement returns the value of the name instead of the generated id. How can I solve this?
I use postgresql, and the table project contains a primary key field "id" with nextval('project_id_seq'::regclass). Here are the POJO, DAO and Resource Classes I use:
public class Project {
private long id;
private String name;
public Project() { // Jackson deserialization }
public Project(long id, String name) {
this.id = id;
this.name = name;
}
...
}
#RegisterMapper(ProjectMapper.class)
public interface ProjectDAO {
#SqlUpdate("insert into project (name) values (:name)")
#GetGeneratedKeys
public long insert(#Bind("name") String name);
}
#Path("/project")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public class ProjectResource {
ProjectDAO projectDAO;
public ProjectResource(ProjectDAO personDAO) {
this.projectDAO = personDAO;
}
#POST
#Timed
public Response add(#Valid Project project) {
long newId = projectDAO.insert(project.getName());
project.setId(newId);
return Response.status(Response.Status.CREATED)
.entity(project).build();
}
}
===============
UPDATE
I just figured out that this relates to the fact that my id column isn't the first column in my table. The column name is. The problem occurs because #GetGeneratedKeys is using org.skife.jdbi.v2.sqlobject.FigureItOutResultSetMapper which is using org.skife.jdbi.v2.PrimitivesMapperFactory which returns org.skife.jdbi.v2.util.LongMapper.FIRST. This mapper is calling
java.sql.ResultSet.getLong(1) through the method extractByIndex(...) to retrieve the generated id, which isn't the id in my case...
I'll fix the issue by reorganizing the columns in the database, but I'd like to have a robust implementation if possible: Is there a way to specify the column name of the id column when using the #GetGeneratedKeys Annotation? (The org.skife.jdbi.v2.util.LongMapper class contains a also method called extractByName(...))
This is an issue in the jdbi implementation and is fixed in a newer version as described in https://github.com/jdbi/jdbi/issues/114

springdata mongo repository method to return specific document property list

using spring data for mongodb, how do I specify the return type of the repository method to include a particular property from the document?
Ex:
#Document (collection = "foo")
class Foo {
String id
String name
... many more attributes
}
repository:
interface FooRepository extends MongoRepository<Foo, String> {
#Query { value = "{}", fields = "{'name' : 1}" }
List<String> findAllNames()
}
Above findAllNames works as expected and fetches only name property from the document. However spring data returned object is a string representation of Foo object which has id and name properties with values and remaining attributes as null.
Instead of Foo objects, I need to fetch List<String> which represents names.
As of now, I used a custom interface to achieve this. Moved the findAllNames() method from Spring data repository interface to my custom interface
interface FooRepositoryCustom {
List<String> findAllNames()
}
interface FooRepository extends MongoRepository<Foo, String>, FooRepositoryCustom {
}
#Component
class FooRepositoryImpl implements FooRepositoryCustom {
#Autowired
MongoOperations mongoOperations;
List<String> findAllNames() {
//using mongoOperations create the query and execute. Return the property values from document
}
}