Converting an #Entity to a projection interface manually - java

So I've been using Spring Data Repositories most of the time. But I've reached a use-case where I cannot use my Spring Repository to retrieve the entity that I need to return to the client.
So I have my class ResourceEntity which is a Spring Data Entity. And I'd like to return this entity as a ResourceProjectioninterface.
#Getter
#Setter
#NoArgsConstructor
#Entity
public class ResourceEntity{
private Long id;
private String name;
private String anotherFieldThatIsNotInTheProjection;
}
public interface ResourceProjection {
Long getId();
String getName();
}
Usually with a Spring Repository, I'd define something like that :
public interface ResourceRepository extends PagingAndSortingRepository<ResourceEntity, Long> {
Optional<ResourceProjection> getById(Long id);
}
In this case I can't use the "automatic proxy" generated by Spring Data to automatically implement my projection with the entity's data.
So my question is : Is there a way to "manually" convert the entity to the projection ?
Another solution I thought of is returning the entity and using Jackson annotations like #JsonIgnore to prevent some of my data to be returned, but that is not optimal with the way my code was written.
Otherwise I can always create a DTO class that will fill up with the data from the Entity. But as I have already created my projection for other purposes, I would like avoid creating a second "DTO".

You can do the projection programmatically in this way:
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
//...
resourceEntity = //find resource
ProjectionFactory pf = new SpelAwareProxyProjectionFactory();
ResourceProjection rp = pf.createProjection(ResourceProjection.class, resourceEntity)

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 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(ResourceEntity.class)
interface ResourceProjection {
#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.
ResourceProjection dto = entityViewManager.find(entityManager, ResourceProjection.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

Implementing interface projection using native sql query
1. OurResourceEntity.java class
#Getter
#Setter
#NoArgsConstructor
#Entity
public class ResourceEntity{
private Long id;
private String name;
}
2. Creating projection Interface name ProjectedResource.java, which maps data collected by the SQL query from repository layer method
public interface ProjectedResource {
Long getId();
String getName();
String getAnotherProperty();
}
3. Creating Repository layer method: getProjectedResources()
We are considering the database table name is resource.
We are only fetching id and name here.But using interface projection we can change the properties name according to our desire.
#Query(name="select id, name, anotherProperty from resource", nativeQuery=true)
List<ProjectedResource> getProjectedResources();
Hope the issue will be resolved!

Related

JPA Projection #OneToMany not working 'Unable to locate appropriate constructor'

I have a large entity-class with many, many fields and a projection-class which should be a part of the large one.
Everything works fine, except the #OneToMany field. The #OneToMany field should be a list of addresses, but when converting it to the projection-class I always get the error "Unable to locate appropriate constructor [...] Expected arguments are: long, [...], ***.entity.Address".
The converter is searching for a single address Object instead of a List of Address Objects, and I don't understand why. I use lombok #Data and #AllArgsConstructor, so Getter and Setter should be there.
#Entity
#Data
public class House implements Serializable {
#Id
#Column(name = "ID", precision = 5)
private Long id;
#OneToMany
#JoinColumn(name = "HouseID")
private List<Address> identAdressen;
}
/// ----------------
#Data
#AllArgsConstructor
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
}
When I remove the "List" in the HouseView-class it works as long as there are only single addresses, but when there are multiple it crashes too.
I don't get it why he tries to find a HouseView-Constructor with an single address-object, when in both classes there are Lists of Addresses.
Let trying not using lombok and see if it works
Spring Data Projections do not support collections 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(House.class)
public interface HouseView {
#IdMapping
Long getObjnr();
Set<AddressView> getIdentAddressen();
#EntityView(Address.class)
interface AddressView {
#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.
HouseView a = entityViewManager.find(entityManager, HouseView.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<HouseView> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
What you can do is to write custom query on HouseRepository as
#Query("SELECT new com.packagename.HouseView(hos) from House hos")
then house view class can be as follows
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
public HouseView(House house) {
identAdressen = house.identAdressen;
}
}
Hope it will work :) You can add more fields into constructor from join tables if required. It might help others coming late on post.

How to make a field as optional in a JPA Entity?

I have Table User in SQL Server DB with close to 30 columns. I have also created an JPA Entity for corresponding DB Table.
The JPA entity looks something like this:
#Entity
#Table(name="USER")
#Setter
#Getter
public class User{
#Id
#Column(name="ID",nullable=false)
private Integer id;
#Column(name="NAME")
private String name;
#Column(name="ADDRESS")
private String address;
#Column(name="FATHER_NAME")
private String fathersName;
}
Now I am trying to create a native query which will only fetch id and name from the User Table.
The Repository class looks something like this.
#Repository
public interface UserDao extends JpaRepository<User,Integer>{
#Query(value=" SELECT u.ID,u.NAME from USER u", nativeQuery=true)
List<User> fetchUser();
}
Now when this is being executed, it is throwing an exception as below:
com.microsoft.sqlserver.jdbc.SqlServerException: The column name address is not valid
When I add the address column to my query this exception goes but then it come for another field.
com.microsoft.sqlserver.jdbc.SqlServerException: The column name father_name is not valid
So, now I want to modify the JPA Entity in such a way that this fields became optional.
I have tried using
#Transient but if some other query is using this address field then it will come as null over there.
I have also tried all the below annotations:
#Basic
#NotFound(action=NotFoundAction.IGNORE)
#Column(nullable="true")
#JsonProperty
But none of this annotations work for me.
You can define additional constructor for the User entity:
#Entity
#Table(name="USER")
public class User {
public User(){
}
public User(Integer id, String name){
this.id = id;
this.name = name;
}
// ...
}
and then write the following query:
#Query("select new your.package.User(u.id, u.name) from User u")
List<User> fetchUser();
Note that this is not native, but jpql query.
Please also note that according to the hibernate documentation:
The projection class must be fully qualified in the entity query, and it must define a matching constructor.
The class here need not be mapped. It can be a DTO class.
If it does represent an entity, the resulting instances are returned in the NEW state (not managed!).
EDIT
You can also try to use spring data jpa projections in the following way:
Define an interface:
interface UserIdWithName {
Integer getId();
String getName();
}
The important thing here is that the properties defined here exactly match properties in the aggregate entity.
Use this interface in the query method:
#Query("select u from User u")
List<UserIdWithName> fetchUserIdWithName();
The query execution engine creates proxy instances of that interface at runtime for each element returned and forwards calls to the exposed methods to the target object. Please note that you can not select only some fields in the query here.
You can use
#Query(value=" SELECT u.ID,u.NAME, '' as address, '' as father_name from USER u", nativeQuery=true)
List<User> fetchUser();
It will set those fields to empty string.
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
Integer 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!

Spring JPA repository interface and default methods use case

I am currently wondering whether a particular use case could be elegantly addressed by using a default interface method inside a JPA repository.
Suppose we have the following entity and supporting types:
public enum Status {
STATUS_1,
STATUS_2,
STATUS_3
}
#Entity
#Getter // from lombok
#Setter // from lombok
public class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "status")
private Status status;
}
I need to be able to query a list of SomeEntity based on combination of Status enumeration values.
With Spring JPA, its respective repository could look like this:
#Repository
public interface SomeRepository extends JpaRepository<SomeEntity, Integer> {
#Query("select s.id, ... from SomeEntity s where s.status in (:statuses)")
List<SomeEntity> getByStatuses(#Param("statuses") List<Status> statuses);
}
The particular combinations of statuses that must be passed, however is very much dependent on the domain of SomeEntity, and I'd like to "bake-in" those statuses in the repository call. Why in the repository -- currently the same repository is used from multiple services, and SomeEntity is updated as part of transactions dealing with other entities updated together according to the service's specifics. The particular getByStatuses calls are the same for all services, and we have duplicated code fragments looking roughly like this:
List<Status> statuses = Arrays.asList(Status.STATUS_1, Status.STATUS_3);
List<SomeEntity> entities = someRepository.getByStatuses(statueses);
....
I was wondering if I could extract the above fragment in a default method inside the repository and change my services to use that method instead:
#Repository
public interface SomeRepository extends JpaRepository<SomeEntity, Integer> {
#Query("select s.id, ... from SomeEntity s where s.status in (:statuses)")
List<SomeEntity> getByStatuses(#Param("statuses") List<Status> statuses);
default List<SomeEntity> getByRelevantStatuses() {
List<Status> relevantStatuses = Arrays.asList(Status.STATUS_1, Status.STATUS_3);
return this.getByStatuses(relevantStatuses);
}
}
My concerns are the "magic" behind the scenes which Spring will do, could it cause issues with an approach like the above?
It appears that the default interface method usage in the described scenario is working. I tried it and so far I have not seen any problems.
I'll still be happy to know if there are things to worry about, and if I find anything useful myself, I will update this post.

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

What is the difference between #Entity and #Repository annotations?

I am new in spring and hibernate and I got these two annotations #Entity and #Repository used for DAO class. As both the annotations are used for the DAO class. Then, what is the difference and when to use one of them.
The #Entity class is the model and the #Repository is the layer that helps you to extract the data from database. For example :
#Entity
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(unique=true)
private String name;
//getters - setters
}
And the repository:
#Repository
public interface StudentRepository extends CrudRepository<Student,Long> {
public Student findByName(String name);
}
The basic CRUD operations are already provided by CrudRepository interface so there is no need to implement them again. You can use them in a Service class like this:
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
public StudentRepository studentRepository;
#Override
public List<Student> findAll()
{
return studentRepository.findAll():
}
#Override
public Student findByName(String name)
{
return studentRepository.findByName(name);
}
}
And in case you want to make custom queries like get a student by name, jpa hibernate is very smart and helps you to only define the method in the #Repository annotated interface and there is no need of any implementations. BUT there is a rule here if you want to make it work. Hibernate will look after method name like this : public Student findByName(String name); the find and Student return type tells hibernate that it have to look for a Student, byName will tell that it have to query the database for a Student with a specific name. (The Name keyword is actually the entity attribute with capital letter ! )
But of course, if you need some more complex queries, there is the #Query annotation that will help you with that :) .
#Entity annotation defines that a class can be mapped to a table, it is just a marker, like for example Serializable interface.
Entity is an object representing (usually) a row in a db.
#Repository annotation defines CRUD operation on table.
It is very like DAO pattern to fetch and save entities from/to storage - it represents db table.
#Entity
Let's say we have a POJO called Student which represents the data of a student and we would like to store it in the database.
public class Student {
// fields, getters and setters
}
In order to do this, we should define an entity so that JPA is aware of it.
So let's define it by making use of the #Entity annotation. We must specify this annotation at the class level.
#Entity
public class Student {
// fields, getters and setters
}
In most typical applications, we have distinct layers like data access, presentation, service, business, etc.
And, in each layer, we have various beans. Simply put, to detect them automatically, Spring uses classpath scanning annotations.
#Repository
#Repository annotates classes at the persistence layer, which will act as a database repository. #Repository’s job is to catch persistence specific exceptions and rethrow them as one of Spring’s unified unchecked exception.
to sum up #Entity is part of JPA Java Persistence API specification used mapping between a java POJO and an entity in relational database world and #Repository is a Spring stereotype used to annotate POJO beans than their jobs is database manipulation operations

Categories

Resources