Select with join into custom type - java

I have entities that look like the following:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MyEntity {
#Id
private UUID id;
#OneToMany(targetEntity = Relation.class, fetch = FetchType.EAGER)
private List<Relation> relatedList;
}
#Entity
#Data
public class Relation {
#Id
private UUID id;
}
In addition, I have another type:
#Data
public class OtherType extends MyEntity {
private String otherField;
public OtherType(UUID id, List<Relation> relations, String otherField) {
super(id, relations);
this.otherField = otherField;
}
}
What I want to do now is to select the objects in the table of MyEntity together with some additional info (otherField) into an object of type OtherType:
select e.id, e.relatedList, 'otherStuff' as otherField from MyEntity e
If I use this query with HQL, it converts e.relatedList to . as col_x_x_, which obviously is a syntax error. I was trying to use a native query, but that just says that OtherType is not an Entity. If I use a NamedNativeQuery with a resultSetMapping, it can't map a list of values to a Collection (No dialect mapping for JDBC type 1111). What I also tried is use the postgres array_agg function to get only an array of IDs for my relation, but that can't be mapped either. Is there any way to achieve this except defining a constructor in OtherType that accepts a single value instead of a list, doing an actual real SQL join (where every instance of Relation adds another MyEntity row), and mapping that afterwards?

This is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
The interesting part for you is, that it supports collections. A sample model could look like the following:
#EntityView(MyEntity.class)
public interface MyEntityView {
#IdMapping
UUID getId();
String getOtherField();
List<RelationView> getRelations();
}
#EntityView(Relation.class)
public interface RelationView {
#IdMapping
UUID getId();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
MyEntityView p = entityViewManager.find(entityManager, MyEntityView.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Related

Fetch only some attributes from table using jpa findall java

I want to select just a few columns from a table.. The catch is that I'm using a specification and pagination from the front end filter, and I don't think I can concatenate those with criteriabuilder. My original idea was to create a #MappedSuperClass with the attributes I wanted (in this case, just the id and date), and fetch using a dao repository from an empty subclass. I have done something similar to this before and it worked, but the subclasses used different tables so it's a different ball game. In this case, since both subclasses use the same table, and there's nothing to differentiate between the classes other than one doesn't have any attributes, it keeps fetching the original bigger class. I want to avoid creating a view with just the columns I want or processing the data in the backend after the fetching, but I think that's the only possible solution.
Superclass
#MappedSupperClass
public class Superclass
{
#Column( name = "id" )
private Integer id;
#Column( name = "date" )
private Date date;
}
Original Subclass
#Entity
#Table( name = "table" )
public class OriginalSubclass
extends Superclass
{
#Column( name = "code" )
private Integer code;
#Column( name = "name" )
private String name;
}
New Subclass
#Entity
#Table( name = "table" )
public class NewSubclass
extends Superclass
{
}
I created a new dao for the new subclass
#Repository
public interface NewSubclassDao
extends JpaRepository<NewSubclass, Integer>, JpaSpecificationExecutor<NewSubclass>
{
}
Is there a way to get only the attributes I want with something similar to my idea?
Or is it possible to do it with criteriabuilder?
If none of the options are viable, would you prefer to use a view or process the data?
EDIT
To make it perfectly clear, I want Spring to bring me only the id and date attributes, using JPA findAll or something very similar, without messing the pagination or filter from the Specification.
You should be able to use #Query to do something like:
#Repository
#Transactional(readOnly = true)
public interface NewSubclassDao
extends JpaRepository<NewSubclass, Integer>, JpaSpecificationExecutor<NewSubclass>
{
#Query("SELECT table.code FROM #{#entityName} table")
public Set<Integer> findAllCodes();
}
There are many ways to do this, but I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
Set<RoleDto> getRoles();
#EntityView(Role.class)
interface RoleDto {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<UserDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

JPA "select new" feature with manytomany relationship: array in constructor signature

I have some entities like this (a post with some tags associated with):
#Entity
public class Post {
#GeneratedId
public Long id;
#ManyToMany(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
public Set<Tag> tags = new HashSet<Tag>();
...
}
#Entity
public class Tag {
#GeneratedId
public Long id;
public String name;
}
I have to make some requests that returns hundreds of Posts with filtering on tags.
Up to now, I have used standard queries returning some Post entities, then accessing the myPost.tags to create my POJO.
This is really time consuming because each time I create a POJO, I call myPost.tags which makes a request to get them. I thought it was a good idea to use the select new feature like this:
select distinct new PostDTO(p.id, t) from Post p left outer join p.tags as t where ...
with
public class PostDTO {
public PostDTO(Long id, Tag[] tags) {...}
}
Unfortunately, due to JPQL: Receiving a Collection in a Constructor Expression the tags could not be passed as an array.
So my question: how can I do? Do I have to change my schema? or JPA has a feature that I've missed?

EclipseLink JPA inheritance without discriminator column

I have a Client and Affiliate class, inheriting from Person class. Joined inheritance strategy type is being used - each of them sharing primary key with the parent class. As there's no discriminator column we chose to use DescriptorCustomizer and ClassExtractor. But it doesn't really give any idea how it works, also, the code doesnt seem to compile. It would be nice if someone gives a nice example with code snippet for understanding.
According to the mentioned documentation:
If you are mapping to an existing database, and the tables do not have
a discriminator column you can still define inheritance using the
#ClassExtractor annotation or <class-extractor> element. The class
extractor takes a class that implements the ClassExtractor
interface. An instance of this class is used to determine the class
type to use for a database row. The class extractor must define a
extractClassFromRow() method that takes the database Record and
Session.
we need to annotate the root entity in a hierarchy with user defined using the class extractor:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#ClassExtractor(PersonClassExtractor.class)
public abstract class Person {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private int age;
// ...
}
Notice that we don't use #Customizer annotations since as this is not required in case of JOINED inheritance strategy:
If a class extractor is used with SINGLE_TABLE inheritance, the rows
of the class type must be able to be filtered in queries. This can be
accomplished by setting an onlyInstancesExpression() or
withAllSubclassesExpression() for branch classes. These can be set
to Expression objects using a DescriptorCustomizer.
The class extractor must be able to determine and return the class type from the database row.
In general we need a replacement of a discriminator column, i.e.
column name unique for a given entity type among others
criteria based on values of a given column of the root entity
Suppose that each of inherited entity type in a hierarchy has a column with unique name:
#Entity
public class Client extends Person {
#Column(name = "CLIENT_SPECIFIC")
private String clientSpecific;
// ...
}
#Entity
public class Affiliate extends Person {
#Column(name = "AFFILIATE_SPECIFIC")
private float affiliateSpecific;
// ...
}
then class extractor may look as follows:
public class PersonClassExtractor extends ClassExtractor {
#Override
public Class<?> extractClassFromRow(Record databaseRow, Session session) {
if (databaseRow.containsKey("CLIENT_SPECIFIC")) {
return Client.class;
} else if (databaseRow.containsKey("AFFILIATE_SPECIFIC")) {
return Affiliate.class;
} else {
return Person.class; // this should never happen
}
}
}
retrieve a list of clients and affiliates
List<Person> polymorphicResults = em.createQuery("SELECT p FROM Person p")
.getResultList();
retrieve a list of affiliates or clients respectively
List<Affiliate> concreteResults = em.createQuery("SELECT a FROM Affiliate a")
.getResultList();
List<Client> concreteResults = em.createQuery("SELECT c FROM Client c")
.getResultList();

How annotation mapping is done in java persistence?

We use annotations for mapping the entity class with the database table by simply specifying #Entity and more like #Id, table joins and many things. I do not know how these entity variables are getting mapped with database table. Can anyone give a short description for understanding.
Thanks :)
Well the idea is to translate your objects and their connections with other objects into a relational database. These two ways of representing data (objects defined by classes and in tables in a database) are not directly compatible and that is where a so called Object Relational Mapper framework comes into play.
So a class like
class MyObject
{
private String name;
private int age;
private String password;
// Getters and setters
}
Will translate into a database table containing a column name which is of type varchar, age of type int and password of type varchar.
Annotations in Java simply add additional information (so called meta data) to your class definitions, which can be read by any other class (e.g. JavaDoc) and in the case of the Java Persistence API will be used by an ORM framework like Hibernate to read additional information you need to translate your object into the database (your database table needs a primary id and some information - like what type of a relation an object has to another - can't be automatically determined by just looking at your class definition).
Annotations are very well explained here:
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
annotations are just metadata on a class, nothing magical. You can write your own annotations. Those annotations are given retention policies of runtime (which means you have access to that metadata at runtime). When you call persist etc the persistence provider iterates through the fields (java.lang.reflect.Field) in your class and checks what annotations are present to build up your SQL statement. Try writing your own annotation and doing something with it. It won't seem very magical after that.
in your case annotation working means mapping with tablename with entity class is look like as ....
#Entity
#Table(name = "CompanyUser")
public class CompanyUserCAB implements java.io.Serializable
{
private long companyUserID;
private int companyID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyUserID")
public long getCompanyUserID()
{
return this.companyUserID;
}
public void setCompanyUserID(long companyUserID)
{
this.companyUserID = companyUserID;
}
#Column(name = "companyID")
public int getCompanyID()
{
return this.companyID;
}
public void setCompanyID(int companyID)
{
this.companyID = companyID;
}
}

HIbernate loads subclasses along with classes

I am using Hibernate to connect to my database.
I have an inheritance structure in my application.The problem is that when i do a query like "from Animal", it does a left outer join for the class Animal,its sub classes and all the associations for Animal and its subclasses.
How do i avoid this situation.I want to load the data only when i specify it through a fetchmode in my criteria query?
Yes, Hibernate supports polymorphic queries. From the documentation:
14.8. Polymorphic queries
A query like:
from Cat as cat
returns instances not only of Cat, but
also of subclasses like DomesticCat.
Hibernate queries can name any Java
class or interface in the from clause.
The query will return instances of all
persistent classes that extend that
class or implement the interface. The
following query would return all
persistent objects:
from java.lang.Object o
The interface Named might be
implemented by various persistent
classes:
from Named n, Named m where n.name = m.name
These last two queries will require
more than one SQL SELECT. This means
that the order by clause does not
correctly order the whole result set.
It also means you cannot call these
queries using Query.scroll().
This is the default behavior (called implicit polymorphism) and Hibernate supports both implicit and explicit polymorphism:
Implicit polymorphism means that instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class. Queries that name the
class will return only instances of
subclasses mapped inside this
<class> declaration as a
<subclass> or <joined-subclass>.
For most purposes, the default
polymorphism="implicit" is
appropriate. Explicit polymorphism is
useful when two different classes are
mapped to the same table This allows a
"lightweight" class that contains a
subset of the table columns.
This can be configured at the class level. Use polymorphism="explicit" if you are if you are using xml mappings, see 5.1.3 Class. Use Hibernate's #Entity annotation if you're using annotations, see 2.4.1. Entity. Below an example:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
#Inheritance(strategy = InheritanceType.JOINED)
public class Foo {
...
}
Assume you have a class structure as follows:
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
then when you select all Animals, you'd expect to also load all Dogs and Cats. After all they are Animals.
A different story are the associations. You can created you mappings such that the associations are lazy load instead of eager load.
Basically it's the default ORM inheritance design pattern used by Hibernate called class inheritance (all the classes are mapped to a single table), if you want to change that you can google:
- single class hierarhy or table per class (this will map every class to a separate table in the DB)
- concrete class hierarhy (this will map only the concrete implementations to a table).
To avoid multiple joins during class hierarchy fetching you can apply SINGLE_TABLE hierarchy mapping strategy, and then define secondary tables on subclasses with SELECT fetching strategy. However, this turns you "heavy join" model into "N+1 select" model. The example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = Super.DISCRIMINATOR_COLUMN, discriminatorType = DiscriminatorType .STRING, length = 255)
public class Super {
public static final String DISCRIMINATOR_COLUMN = "classname";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
#Column(updatable = false, insertable = false)
protected String classname;
protected String superProp = "superProp";
public long getId() {
return id;
}
public String getClassname() {
return classname;
}
public String getSuperProp() {
return superProp;
}
public void setSuperProp(String superProp) {
this.superProp = superProp;
}
}
#Entity
#SecondaryTable(name = SubA.TABLE)
#Table(appliesTo = SubA.TABLE, fetch = FetchMode.SELECT)
public class SubA extends Super {
public static final String TABLE = "SUBA";
#Column(table = TABLE)
protected String subAProp = "subAProp";
public String getSubAProp() {
return subAProp;
}
public void setSubAProp(String subAProp) {
this.subAProp = subAProp;
}
}
#Entity
#SecondaryTable(name = SubB.TABLE)
#Table(appliesTo = SubB.TABLE, fetch = FetchMode.SELECT)
public class SubB extends Super {
public static final String TABLE = "SUBB";
#Column(table = TABLE)
protected String subBProp = "subBProp";
public String getSubBProp() {
return subBProp;
}
public void setSubBProp(String subBProp) {
this.subBProp = subBProp;
}
}
And what SQL is done on from Super HQL query:
select [...] from SUPER super0_
select super_1_.subaprop as subaprop1_83_ from SUBA super_1_ where super_1_.id=1
select super_2_.subbprop as subbprop1_84_ from SUBB super_2_ where super_2_.id=2
More about this approach and general hibernate performance hints you can read in my article.

Categories

Resources