Does Hibernate (JPA) allow to map custom columns (i.e. Aliased columns) - java

I am having simple entity with few properties as shown in image.
Query as follows
select *,concat(current_date - cre_dte::date) as _days_ago
from test."general"
where (current_date - cre_dte::date)=7 or (current_date - cre_dte::date)<11
My Entity is
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
#Data
#Entity
#Table(name = "general", schema = "test")
public class GeneralEntity implements Serializable {
private static final long serialVersionUID = -2343243243242432341L;
/**
* id
*/
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
/**
* loadNumber
*/
#Column(name="load_number")
private String loadNumber;
/**
* createdDate
*/
#Column(name="cre_dte")
private Date createdDate;
#Formula(value = "_days_ago")
private String _daysAgo;
}
I am trying to map entities to store _days_ago from my native query to pojo. for that I modified pojo to add #Formula. but seems like does not mapping this alias to my property. In my JPA interface my method is like below
#Query(nativeQuery = true,value = "select *, concat(current_date - cre_dte::date) as _days_ago from general where (current_date - cre_dte::date)=7 or (current_date - cre_dte::date)<11")
public List<GeneralEntity> getFewDaysAgo() throws Exception;

You can use #Formula for this purpose like:
public class GeneralEntity implements Serializable {
...
#Formula("current_date - cre_dte::date")
Integer daysAgo;
}
but I generally don't recommend that as that will be executed every time you fetch a GeneralEntity entity.
Usually this is best handled by using a custom DTO projection for this use case. You could use something like the following:
class GeneralEntityDto {
Long id;
String loadNumber;
Date createdDate;
String daysAgo;
// constructor + getters
}
and a query like this:
#Query("select new com.mycompany.package.GeneralEntityDto(g.id, g.loadNumber, g.createdDate, concat(current_date - cast(g.createdDate as date)) from GeneralEntity where (current_date - cast(g.createdDate as date))=7 or (current_date - cast(g.createdDate as date))<11")
public List<GeneralEntityDto> getFewDaysAgo();
If you have more complex needs like the need to map collections, you should give Blaze-Persistence Entity Views a shot.
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(GeneralEntity.class)
public interface GeneralEntityDto {
#IdMapping
Long getId();
String getLoadNumber();
Date getCreatedDate();
#Mapping("DAY_DIFF(CAST_DATE(createdDate), current_date)")
String getDaysAgo();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
GeneralEntityDto a = entityViewManager.find(entityManager, GeneralEntityDto.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
public default List<GeneralEntityDto> getFewDaysAgo() {
return findAll((root, query, criteriaBuilder) -> {
Expression e = criteriaBuilder.function("DAY_DIFF", root.get(createdDate).as(java.sql.Date.class), criteriaBuilder.currentDate());
return criteriaBuilder.or(
criteriaBuilder.equal(e, criteriaBuilder.literal(7)),
criteriaBuilder.lessThan(e, criteriaBuilder.literal(1))
);
});
}
public List<GeneralEntityDto> findAll(Specification s);

Related

Projections not being applied after upgrading to Spring Boot 2.5.4

For some reason, Projections are not being applied any longer after upgrading to Spring Boot 2.5.4. I really can't figure out why.
I have a class WhoDidWhatWhen (I've changed the class / variable names in the example):
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class WhoDidWhatWhen {
#Id
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
private User who;
#NotNull private String what;
#NotNull #PastOrPresent private Date when;
}
and I have a projection in the same package as the above class:
#Projection(
name = "whoDidWhatWhenProjection",
types = {WhoDidWhatWhen.class})
public interface WhoDidWhatWhenProjection {
#Value("#{target.id}")
long getId();
User getWho();
String getWhat();
Date getWhen();
}
and finally I have my RestRepository:
#RepositoryRestResource(exported = false, excerptProjection = WhoDidWhatWhenProjection.class)
public interface WhoDidWhatWhenRepository
extends PagingAndSortingRepository<WhoDidWhatWhen, Long>, JpaSpecificationExecutor<WhoDidWhatWhen> {}
For some reason the projection / excerpt just isn't being picked up and applied and I just can't figure out why.
I even tried to manually register to projection, but to not avail:
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.getProjectionConfiguration().addProjection(WhoDidWhatWhenProjection.class);
}
}
Has anyone experienced this before?
I don't know an answer to your particular problem, but I would like to offer you an alternative, which is Blaze-Persistence Entity Views. You can imagine this to be like Spring Data Projections on steroids, with mappings that affect the SQL and thus improve performance. Mappings are fully type validated to avoid runtime surprises and the integration is seamless.
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(WhoDidWhatWhen.class)
public interface WhoDidWhatWhenProjection {
#IdMapping
Long getId();
String getWhat();
Date getWhen();
UserDto getWho();
#EntityView(User.class)
interface UserDto {
#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.
WhoDidWhatWhenProjection a = entityViewManager.find(entityManager, WhoDidWhatWhenProjection.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<WhoDidWhatWhenProjection> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

How do I populate two fields of entity using Hibernate?

I am building an API to return two fields as such:
{
currentPoints: 325,
badgeName: "Some Badge"
}
However, I am having trouble using hibernate in order populate those two fields. I made two attempts and both are throwing errors. Both of these errors can be found in their respective Repository file. In the 2nd attempt, I am using native=true and am able to get it to work using a SELECT *. However, I am trying to only populate and return two fields of the entity.
One solution I thought about is using the 2nd approach with a SELECT * and creating another package named response with CurrentInfoResponse class and just returning that class. However, I wanted to see if there was a way to avoid this using the current model that I have.
Possible Solution:
#Getter
#AllArgsConstructor
public class CurrentInfoResponse{
private Integer currentPoints;
private String badgeName
}
Package Structure:
Controller.java:
#GetMapping("/current-badge/{userId}")
public CurrentBadgeInfoModel getCurrentBadge(#PathVariable Integer userId){
return currentBadgeInfoService.getCurrentBadge(userId);
}
ServiceImpl.java:
#Override
public CurrentBadgeInfoModel getCurrentBadge(Integer userId){
return currentBadgeInfoRepository.getCurrentBadge(userId);
}
CurrentBadgeInfoModel.java:
#Getter
#Entity
#Table(name = "user_current_badge_info")
public class CurrentBadgeInfoModel {
#Id
#Column(name = "user_current_info_id")
private Integer userCurrentBadgeInfo;
#Column(name = "user_id")
private Integer userId;
#Column(name = "current_points")
private Integer currentPoints;
#ManyToOne
#JoinColumn(name = "badge_id")
private BadgeModel badgeModel;
}
BadgeModel.java
#Getter
#Entity
#Table(name = "badge_info")
public class BadgeModel {
#Id
#JoinColumn(name= "badge_id")
private Integer badgeId;
#Column(name = "badge_name")
private String badgeName;
}
Repository.java - ATTEMPT 1:
#Repository
public interface CurrentBadgeInfoRepository extends JpaRepository<CurrentBadgeInfoModel, Integer> {
#Query("SELECT cbim.currentPoints, cbim.badgeModel.badgeName FROM CurrentBadgeInfoModel cbim JOIN
cbim.badgeModel WHERE cbim.userId=?1")
CurrentBadgeInfoModel getCurrentBadge(Integer userId);
}
//Error: No converter found capable of converting from type [java.lang.Integer] to type [com.timelogger.model.CurrentBadgeInfoModel]
Repository.java - ATTEMPT 2:
#Repository
public interface CurrentBadgeInfoRepository extends JpaRepository<CurrentBadgeInfoModel, Integer> {
#Query(value = "SELECT current_points, badge_name FROM user_current_badge_info ucbi JOIN badge_info bi ON ucbi.badge_id=bi.badge_id WHERE user_id=?1", nativeQuery = true)
CurrentBadgeInfoModel getCurrentBadge(Integer userId);
}
//Error: Column 'user_current_info_id' not found
Using the SELECT clause of HQL should help you here.
If you don't have that constructor, you can add it
#Query("SELECT new CurrentBadgeInfoModel(cbim.currentPoints, cbim.badgeModel.badgeName) FROM CurrentBadgeInfoModel cbim JOIN
cbim.badgeModel WHERE cbim.userId=?1")
Notice the usage of new CurrentBadgeInfoModel(cbim.currentPoints, cbim.badgeModel.badgeName)
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(CurrentBadgeInfoModel.class)
public interface CurrentInfoResponse {
Integer getCurrentPoints();
#Mapping("badgeModel.badgeName")
String getBadgeName();
}
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
CurrentInfoResponse findByUserId(Integer userId);
The best part is, it will only fetch the state that is actually necessary!

Hibernate : Genereting dynamic entity at runtime

I need to create entity based on information from database. Based on database I created string like this :
` package az.com.ds.entity.crudEntity;
import javax.persistence.Table;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Column;
#Table(name = "CMN_SP", schema = "CMN")
#Entity
public class CmnSpEnt {
#Id
private Integer id;
#Column(name = "NAME")
private String name;
} `
Then I created java file based on this string and compiled it at runtime. Everything works perfectly to this step. But when I want to get data based on entity it throws exception as
org.hibernate.hql.internal.ast.QuerySyntaxException: CmnSpEnt is not mapped [Select x from CmnSpEnt x ].
Now I need to map entity for hibernate in order to get data from database. Is there a way to accomplish this?

What is #StaticMetamodel and SingularAttribute<Obj,Obj>?

I am trying to figure out this code for about two hours now, for example in below class what are these fields representing?
import java.util.Date;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
import java.util.UUID;
#StaticMetamodel(Address.class)
public class Address_ extends {
public static volatile SingularAttribute<Address, Long> id;
public static volatile SingularAttribute<Address, UUID> personId;
public static volatile SingularAttribute<Address, Person> person;
}
The Address.class is a java class which has the following definition:
#Entity
#Table(name = "address", schema = "public")
public class Address{
private Long id;
private Person person;
private UUID personId;
//....
}
Can you, please, explain what are the #StaticMetamodel and #SingularAttribute annotations used for? It might be simple but I can't understand.
As per documentation:
A static metamodel is a series of classes that "mirror" the entities
and embeddables in the domain model and provide static access to the
metadata about the mirrored class's attributes.
The static metamodel has the following properties:
For each managed class X in package p, a metamodel class X_ in package p is created.
For every persistent non-collection-valued attribute y declared by class X, where the type of y is Y, the metamodel class must contain a declaration as follows:
SingularAttribute example:
public static volatile SingularAttribute<X, Y> y;
The static metamodel is useful for creating type-safe queries with the JPA's Criteria API.
For example, let's have the following two entities, Order and Item:
#Entity
public class Order {
#Id
#GeneratedValue
Integer id;
#ManyToOne
Customer customer;
#OneToMany
Set<Item> items;
BigDecimal totalCost;
// accessors
}
and the Item entity:
#Entity
public class Item {
#Id
#GeneratedValue
Integer id;
int quantity;
#ManyToOne
Order order;
// accessors
}
Here's a typesafe criteria query, build with the Criteria API:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
SetJoin<Order, Item> itemNode = cq.from(Order.class).join(Order_.items);
cq.where(cb.equal(itemNode.get(Item_.id), 5)).distinct(true);
Note the usage of Item_.id and Order_.item. Those access statically the static meta-model properties (which mirror the entity properties) and this way it's ensured that the query is build properly.
I've been thinking about this a lot lately as I've been trying to learn and understand JPA. I believe I have an answer to your question: Why do we need MetaModels, and why can't we just use the Entity Model?
Take a look at this entity:
#Entity
public class Item {
#Id
#GeneratedValue
Integer id;
int quantity;
#ManyToOne
Order order;
// accessors
}
Note that none of the properties on the Entity have the keyword static. That means that in order to use them, we need to create a new Object.
When we are building queries with CriteriaBuilder, we don't need to create an object... we just want to use the properties on the Entity to generate our query. This is the reason we have MetaModels! They create static properties that we can access without having to create an object. So we can can do things like Konstantin mentioned:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
SetJoin<Order, Item> itemNode = cq.from(Order.class).join(Order_.items);
cq.where(cb.equal(itemNode.get(Item_.id), 5)).distinct(true);
Here, we aren't making an "Item" object... we just need to know the properties of it. The static properties on the MetaModel enable us to do so!

Insert to DataBase Using EntityManager

I've been looking for a way to interact with my database using EntityManager class in Java.
The question is related to these two tables I have defined in my DB:
PARENT_TABLE:
PK_ONE
PK_TWO
CHILD TABLE:
PK_ONE
COLUMN
PK_TWO
Here is something I have so far.
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;
import javax.persistence.PrimaryKeyJoinColumn;
#Entity
#Table(name="PARENT_TABLE")
#SecondaryTable(name="CHILD_TABLE", pkJoinColumns={
#PrimaryKeyJoinColumn(name="PK_ONE"),
#PrimaryKeyJoinColumn(name="PK_TWO")})
public class ExampleTbl implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PK_ONE")
private String pkOne;
#Id
#Column(name="COLUMN", table="CHILD_TABLE")
private String column;
#Column(name="PK_TWO")
private String pkTwo;
//GETTERS AND SETTERS
}
My questions are:
To be able to Insert to the PARENT_TABLE a new row, do I have to create a new Entity class with just two fields (PK_ONE, PK_TWO) to be able to use the merge() or persist() method?
Will I have to create another Entity class to Insert a new row to my CHILD_TABLE?
To retrieve a List with the existing data I have a method something like this:
#Transactional(readOnly = true)
public List<ExampleTbl> getFoldersList() {
em = emf.createEntityManager();
Query q = em.createQuery("SELECT e FROM ExampleTbl e WHERE e.pkTwo = :pkTwo ORDER BY e.pkOne");
q.setParameter("pkTwo", "My Test");
List<ExampleTbl> result = q.getResultList();
return result;
}
Do you think this is the best way to do it?
I think its a question with a couple of possible answers. I would recommend reviewing some of the ways they set things up in the Java EE 6 tutorial examples. You can read about that at http://docs.oracle.com/javaee/6/tutorial/doc/giqst.html

Categories