Use proxies with Hibernate runtime bytecode enhancement - java

I'm using Spring Boot 2.7.5 with Hibernate 5.6.12.Final and apply bytecode enhancement at build-time via hibernate-enhance-maven-plugin. It works great and prevents eagerly fetching #OneToOne relationships, as described here https://vladmihalcea.com/hibernate-lazytoone-annotation/.
However, I found that all lazy relationships do not use proxies anymore but are fetched from the database when I use the getter (which has been instrumented by the maven plugin at build time). Basically, what I would like to do is to still use proxies with bytecode enhancement. Something similar is available at runtime https://in.relation.to/2019/07/30/bytecode-proxy/, but I can't find a way to do it at build-time.
For a bit of context, I need proxies for lazy-loaded properties because it allows me to apply custom logic in entity->dto mapper (using Mapstruct) based on whether the proxy has been initialized or not. In some cases, I must trigger an initialization manually if a Hibernate session is available (and prevent LazyInitializationException).
Any suggestion is greatly appreciated!

If you want to avoid LazyInitializationException for your DTO mapping, you should rather try to avoid doing the mapping in Java code and instead use a solution that can take the mapping down to the HQL/SQL level.
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 a possible 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!

Related

Hibernate Bytecode Instrumentation: Set fetched property values to their fields right away

I'm working on an enterprise application that uses Hibernate and EJB, and I'm utilizing Hibernate's Bytecode instrumentation to implement true lazy-loading of properties having bidirectional #OneToOne associations. I have a service method implemented using EJB, and as we all know, EJB uses RMI, which uses native Java serialization and deserialization to facilitate RPC invocations. The service method returns an entity with needed properties already fetched using JPQL, but since Hibernate doesn't set the fetched properties eagerly to their target fields, clients calling the service method end up receiving entities with properties having null values. Aside from calling the property getters manually before returning the entity, is there a way to tell Hibernate to set fetched property values automatically to their corresponding fields?
I'm using Hibernate 5.3.15 and JBoss EAP 7.2.8.
First of all, EJB doesn't necessarily use RMI, I guess what you mean is EJB remoting. There is no way that I know of to force field initialization except for initializing the fields through some means (access). One way to overcome this is to use DTOs that simply do not do any lazy initialization.
I think that this might be a bug in the serialization code of Hibernate for such bytecode enhanced proxies, so please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.
If you want to take the DTO approach, 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();
UserDetailsDto getDetails();
#EntityView(UserDetails.class)
interface UserDetailsDto {
#IdMapping
Long getId();
String getFirstname();
}
}
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!

Spring projection with entity inside

I need in metainfo for entity (hierarchy level from recursive sql query) so i created next projection
#Value
public class ProjectionObject{
MyEntity entity;
int metainfo;
}
#Query(value = "select my_entity.*, 1 as metainfo from my_entities", nativeQuery = true)
List<ProjectionObject> findSome();
But it returns List<List> but i expect List.
As result i what to manipulate with ProjectionObject#entity as with managed (by Entity Manager) ProjectionObject#entity, in other word i want to get managed entity with metainfo once without getting f.e. hierarchy Ids and after get entities
I'm not sure Spring Data Projections supports that.
However, this is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence 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 projection with Entity Views could look as simple as the following
#EntityView(MyEntity.class)
interface ProjectionObject {
#Mapping("this")
MyEntity getEntity();
#Mapping("1")
int getMetaInfo();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ProjectionObject dto = entityViewManager.find(entityManager, ProjectionObject.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
List<ProjectionObject> findAll();
You can also make use of updatable entity views which allows you to eliminate the entity type completely, which reduces the amount of data fetched and flush back only the parts that you actually want to change:
#UpdatableEntityView
#EntityView(MyEntity.class)
interface ProjectionObject {
#IdMapping
Integer getId();
String getName();
void setName(String name);
#Mapping("1")
int getMetaInfo();
}
Now you can fetch that object and then after changing the state flush it back to the database:
ProjectionObject o = repository.findOne(123);
o.setName(o.getName().toUpperCase());
repository.save(o);
And it will only flush back the name as you will see in the SQL.

How to Convert a JQPL Query into a DTO?

I need to know if it's possible for me to convert my JQPL query result into a DTO.
The query result is a Array of Arrays like this Json:
[
[
ModuleID: number,
ModuleName: string,
ToolId: number,
ToolName: string,
Enabled: boolean
],
]
And I want to convert into this DTO:
public class ModuleDTO {
private Long ModuleID;
private String ModuleName;
private List<ToolsDTO> Tools;
}
public class ToolsDTO {
private Long ToolId;
private String ToolName;
private Boolean Enabled;
}
You can see that the last three are children of the module, that means that in the search there may be repeated modules, but all children must be within the same list.
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.
You didn't specify an entity model so I'm going to assume some things here. A mapping could look as simple as the following
#EntityView(Module.class)
interface ModuleDTO {
#IdMapping
Long getModuleId();
String getModuleName();
List<ToolsDTO> getTools();
}
#EntityView(Tools.class)
interface ToolsDTO {
#IdMapping
Long getToolId();
String getToolName();
Boolean getEnabled();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ModuleDTO dto = entityViewManager.find(entityManager, ModuleDTO.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

How to specify spring data jpa request to load collection of entity properties?

I use spring data JPA. I need in my repository request to load only collection of concrete properties colors:
#Query(value = "SELECT cd.color FROM CalendarDetails cd where cd.userCalendar.userId = :userId")
List<String> findCalendarColorsByUserWithDuplicates(#Param("userId") Long userId);
Provided solution works correctly.
I want simplify it using spring approach to load collection of the repository objects I'd use (repository public interface CalendarDetailsRepository extends JpaRepository<CalendarDetails, Long>):
List<CalendarDetails> findByUserCalendarUserId(#Param("userId") Long userId);
But I need collection of colors! Trying
List<String> findColorByUserCalendarUserId(Long userId);
I get collection of CalendarDetails
Is it possible to improve my last request following spring data approaches to load list of colors?
You can try special Projection mechanisms that Spring Data provides. It will allow you not only to optimize your queries but also to make it with pure java without using #Query.
There are a lot of ways to
make it, but I would recommend the following.
You add an interface that contains getters for the properties that you need to take from entity:
public interface ColorOnly {
String getColor();
}
Then you return the list of this interface' objects:
List<ColorOnly> findColorByUserCalendarUserId(Long userId);
To use the colours from the interface, you just invoke getColor method. You may consider simplifying it with Java 8 streams and map conversions. BTW, this one will only query colour. No other fields will be included into the query Hibernate produces.
Try to add All
findAllByUserCalendarUserId(Long userId);
BTW, IntelliJ IDEA provide very deep support of JPA repositories, so it's prevent a lot of possible issues when you create queries like this one

Filtering data of all query

I create a new application with Spring and Mysql (but I am relatively free to use others things)
In this application, user logs on can not see all data (except user 'admin').
There is a tree of group, users are in group. An user can only see users (or others objects) of his group (or descendant group).
I try to find idea to do that "elegantly":
For example, if i write: productDao.findAll(), it return all product allready filtered without having rewrite all request in all DAO.
If it does not exist, I accept all kind of idea: refactoring the database? change mysql for other database, not use JPA, other.... Or may be, I'm on the wrong way and it's a bad idea to do something like that...
Use Spring Security 4. It supports Roles and Hierarchical Roles too
Use Spring Data JPA to create your DAOs. It integrates with Spring Security 4
Here's an example of writing a DAO using Spring Data JPA. You write the interface and SDJ creates the class for you.
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query("select e from #{#entityName} e where e.owner = ?#{principal?.username}")
Page<Product> findAll(Pageable pageable);
}
In reality, you'd do this in your Base Repository and extend that.
You need to create a bean extending EvaluationContextExtensionSupport for the
?#{principal?.username} security expression to work

Categories

Resources