hibernate projection for one-to-many mapping set of objects - java

I have a USER object which has many fields having one-to-many mapping with PERMISSION object as well.
I want to fetch only few fields of user with a set of permissions as well. my code is
public class USER {
private Integer id;
private String username;
...
...
...
private Set<Permission> permissions = new HashSet<Permission>();
//setter - getter methods
}
Permission.java
public class Permission {
private String name;
private Integer id;
//setter - getter methods
}
Projection code:
Criteria criteria = session.createCriteria(User.class);
criteria.setCacheable(true);
criteria.add(eq("username", username).ignoreCase());
criteria.createAlias("permissions ", "perm", LEFT_OUTER_JOIN);
ProjectionList projectedFields = Projections.projectionList();
projectedFields.add(Projections.property("id").as("id"));
projectedFields.add(Projections.property("perm.id").as("permissions.id"));
criteria.setProjection(projectedFields);
criteria.setResultTransformer(new AliasToBeanNestedResultTransformer((User.class)));
User user = (User) criteria.uniqueResult();
I'm getting the following exception:
org.hibernate.PropertyNotFoundException: Could not find setter for id on interface java.util.Set
at org.hibernate.property.ChainedPropertyAccessor.getSetter(ChainedPropertyAccessor.java:66)
at org.hibernate.transform.AliasToBeanResultTransformer.initialize(AliasToBeanResultTransformer.java:121)
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:84)
at ae.gov.adm.saeed.util.AliasToBeanNestedResultTransformer.transformTuple(AliasToBeanNestedResultTransformer.java:80)
at org.hibernate.transform.CacheableResultTransformer.retransformResults(CacheableResultTransformer.java:230)
any idea, how to resolve this issue?

This doesn't work. This only works for scalar results, but not for nested structures. You would have to build the object graph yourself.
Anyway, this is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persitence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure 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.
A mapping for your model could look as simple as the following
#EntityView(User.class)
interface ApplicationUser {
Integer getId();
#Mapping("permissions.id")
Set<Integer> getPermission();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ApplicationUser dto = entityViewManager.find(entityManager, ApplicationUser.class, id);
But 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
It will only fetch the mappings that you tell it to fetch

Related

Hibernate #Formula which return collection

I'm using a legacy database. In my example, we retrieve a product which have some characteristics. In the db, we can find a product table, a characteristic table and a jointable for the manyToMany association.
The only field i need is the label of the characteristics. So, my Product entity will contains a list of characteristics as String. I would like to not create to many entities in order to not overload my sourcecode. Let's see the example :
#Entity
#Table(name = "product")
public class Product implements Serializable {
#Id
#Column(name = "id")
private Long id;
// all field of Product entity
#ElementCollection(targetClass = String.class)
#Formula(value = "(SELECT characteristic.label FROM a jointable JOIN b characteristic ON jointable.characteristic_id = characteristic.id WHERE jointable.product_id = id)")
private Set<String> characteristics = new HashSet<>();
// Getter / setter
}
To represent my characteristics, i tried to use the association of #Formula and #ElementCollection. As you can see, the names of tables (a and b in the query) does not match with my representation of these datas.
But, when I try to load a product, I get an error like "PRODUCT_CHARACTERISTICS table not found".
Here the generated SQL query executed by hibernate :
SELECT product0_.id AS id1_14_0_,
-- Other fields
characteri10_.product_id AS product_1_15_1__,
(SELECT characteristic.label
FROM a jointable JOIN b characteristic ON jointable.characteristic_id = characteristic.id
WHERE jointable.product_id = id) AS formula6_1__,
FROM product product0_
-- Other Joins
LEFT OUTER JOIN product_characteristics characteri10_ ON product0_.cdprd = characteri10_.product_cdprd
WHERE product0_.id = ?;
In the FROM part, we can refind the call of product_characteristics table (which not exist in the database).
So, my main question is the following : How can I get the list of characterics as entity attribute ? Can I reach this result with #Formula ?
Edit
In other words, i would like to load only one attribute from Many to Many mapping. I found an example here but it works only with the id (which can find in the jointable)
I assume that what you want to achieve here is reducing the amount of data that is fetched for a use case. You can leave your many-to-many mapping as it is, since you will need DTOs for this and 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(Product.class)
public interface ProductDto {
#IdMapping
Long getId();
String getName();
#Mapping("characteristics.label")
Set<String> getCharacteristicLabels();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ProductDto a = entityViewManager.find(entityManager, ProductDto.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<ProductDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

Reducing model #Entity bloat with Spring Hibernate and MSSQL using DTO + Stored Proc

Edit: I think it would be helpful to explain my goal here first. My goal is to reduce and avoid model/#Entity bloat when using stored procedures with Hibernate. You can get raw data back from the persistent EntityManager when using a stored procedure, but that data will not be mapped. If you send in a stripped down model to Hibernate, Hibernate will only send you back the columns which are annotated as #Column on the #Entity model (almost forcing you to create a new #Entity for every stored procedure!) You can attempt to map this data with a DTO that has more properties, but they won't map to anything because all the fields which were not included on the model will return null.
I've been struggling to find an answer to this in my research. We use an MSSQL database and Spring with JPA/javax/Hibernate persistence, but do not rely on Hibernate for its ORM. All CRUD operations are done using stored procedures. We have several models (Spring #Entity) which work well for retrieving and mapping data. For example, a basic user model.
import javax.persistence;
#Entity
public class User {
#Id
#Column
private int userID;
#Column
private String userName;
#Column
private Date userDOB;
public User(UserDTO userDTO){
userName = userDTO.getUserName();
userID = userDTO.getUserID();
// Extra column userDOB, and no way to map accountDetails from the DTO
}
public int getUserID(){...}
public String getUserName(){...}
public Date getUserDOB(){...}
}
This works well when the stored procedure selects columns in a way that matches up with the model, however in cases where we want to selectively query data using joins, the columns (names and number of columns) often don't match up with the models. In this case, it makes sense to have DTOs to actually map the receiving data from the database using a constructor in the related Model. However, Spring doesn't like injecting the DTO directly (because it complains it isn't an entity), and injecting the model directly into the stored procedure query will fail, since the columns (number of columns!) don't match up.
public class UserDTO {
private String accountDetails;
private int userID;
private String userName;
public int getUserID(){...}
public String getUserName(){...}
}
I've tried using ModelMapper, but the first argument is an Object (the data), which isn't obtainable, since the data can't be mapped. I can of course call StoredProcedureQuery without a Model hint, and receive the raw data, but it will not be mapped.
public class UserRepo {
import javax.persistence;
public List<UserDTO> getUserAccountInfo(){
StoredProcedureQuery query = entityManager.createQuery(storedProcedureSelectUserAccount, User.class);
query.execute(); // Will fail here with SQL Server error: Unknown column userDOB.
List<UserDTO> result = query.getResults();
return result;
}
}
Others have suggested using raw Selects or other String based mapping strategies, but I would really appreciate some advice on retrieving and mapping the returned data using a DTO with the already written Stored Procedures. Thank you!

How to get only ChildIds(Instead of whole child Object) from parent entity in Hibernate Jpa?

I have two tables, parent and child, having a relation
#OneToMany. How to get only child ids (instead of whole child Object) from parent entity in Hibernate JPA?
#Entity
Class Parent{
#Id
private Integer id;
private String agreementId;
#OneToMany
List<Child> child
}
#Entity
Class Child{
#Id
private Integer id;
#ManyToOne
List<Parent> parent;
}
Output Excepted: { id: 1; agreementId:"12345"; child:[1,2,3,4] }
DataBase Used is Postgresql
You will need a DTO for this which requires that you also flatten the result manually. 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(Parent.class)
public interface ParentDto {
#IdMapping
Integer getId();
String getAgreementId();
#Mapping("children.id")
Set<Integer> getChild();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ParentDto a = entityViewManager.find(entityManager, ParentDto.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<ParentDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
According to what i know, the answer is you can't. Hibernate mapping by entity so you always have the whole child object.
I don't know what you want to do but i think you should just get the whole child list object then modify it (maybe by a stream.map() function) to become a list childId as you want

Quarkus: how to update an entity with a new instance?

I am working on an application according to the Domain Driven Design. For this reason my fetched entities are mapped to the domain model. A modification to this domain model might have to be updated, so I map the domain model back to the entity (new instance) and try to persist it. This is when Hibernate slaps me on the wrist with a PersistentObjectException:
detached entity passed to persist
I tried to recreate this problem in a Spring Boot application, but for some reason Spring connects the new Entity instance to the attached instance. (Or so it seems.)
Summarizing: Entity (attached) -> Model -> Entity (detached)
Here I have a simple example project which faces the same issue:
https://gitlab.com/rmvanderspek/quarkus-multithreading/-/tree/persistence
UPDATE: The following does 'the trick', but it is a workaround and feels like there might be side-effects I didn't bargain for:
entity = respository.getEntityManager().merge(entity);
repository.persist(entity);
The problem is when an entity has a generated id, but you set a value on a new instance created through a constructor. Such entities must use merge for applying the changes to the persistent state. The other way around would be to use entityManager.find() first to retrieve the managed entity and apply the changes on that object.
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/domain model could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
#UpdatableEntityView
public interface UserDto {
#IdMapping
Long getId();
String getName();
void setName(String name);
#UpdatableMapping
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 user = entityViewManager.find(entityManager, UserDto.class, id);
user.getRoles().add(entityViewManager.getReference(RoleDto.class, roleId));
entityViewManager.save(entityManager, user);
This will only flush the data that actually changed and also avoid unnecessary loads.
The Quarkus and JAX-RS integration make it super simple to use it in your application: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/#jaxrs-integration
#POST
#Path("/users/{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Transactional
public Response updateUser(#EntityViewId("id") UserDto user) {
entityViewManager.save(entityManager, user);
return Response.ok(user.getId().toString()).build();
}

Get collection count instead of the collection in JPA Criteria API?

Lets say I have an entity:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
#ManyToMany(fetch = FetchType.LAZY)
private List<Permission> permissions;
// etc
// other fields here
}
I want a to build a query using the Criteria API that filters these users and shows a list of people and among other info from the entity - how many roles does a person have.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> personRoot = query.from(Person.class);
// predicates here
However, this limits me to returning only a list of Person entities. I can always add a #Transient field to the entity, but this seems ugly since I might have many different queries and might end up with many such fields.
On the other hand - I cant use HQL and write the query since I want complex filtering and I would have to deal with appending and removing things from the HQL query.
My question, besides the one in the title of this post is this: how do I query the database using the Criteria API and return a non-entity (in case I want to filter the Person table but return only the number of roles, permissions, etc) and how do I do it for something very close to the actual entity (like the example with the role counter instead of the roles collection)?
UPDATE
Using Hibernate's projections I came up with this. But still don't know that to write in TODO. Projections.count doesn't work since it excpects some kind of grouping, and I don't seem to be able to find any examples in the Hibernate documentation.
Criteria cr = session.createCriteria(Person.class);
if (id != null) {
cr.add(Restrictions.eq("id", id));
}
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("id"), "id");
projectionList.add(TODO, "rolesCount");
CriteriaQuery<Long> query = entityManager.getCriteriaBuilder().get().createQuery(Long.class);
query.select(builder.get().countDistinct(root));
works for me:)
how do I do it for something very close to the actual entity (like
the example with the role counter instead of the roles collection
You could make these values properties of your User entity by various means, for example using a Hibernate #Forumula property. This will issue an inline subquery on Entity load to get the count without touching the collection.
#Formula("select count(*) from roles where user_id = ?")
private int numberOfRoles;
Another (JPA compliant) option is to handle these calculated fields by creating a view at the database level and the mapping this to your User:
e.g.
#OneToOne
private UserData userData; //entity mapped to your view (works just like a table)
....
public int getNumberOfRoles(){
return userData.getRoleCOunt();
or
by using #SecondaryTable to join this User data.

Categories