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!
Related
I'm doing a spring boot experiment and using MySQL.
For example, if I have a list of users, but I want to get the specified names, how can I write the SQL query that only indicates this situation?
This is my model class :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name="users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long id;
#Column(name="first_name")
public String name;
#Column (name="last_name")
public String last_name;
}
This is my JPA interface :
public interface CommentRepository extends JpaRepository<User , Long >{
// All C.R.U.D database methods
}
Finally, my controller area is as below :
#RestController
#RequestMapping(path="/api/v1/users")
public class CommentController {
#Autowired
CommentRepository repository ;
#GetMapping(path="/list")
public List<User> users() {
return repository.findAll();
}
}
Maybe you didn't understand my problem, I just want to write a customizable query of my own.
For example, I want to pull the data with the method I designed, while I normally pull the data by numbers with the find by id method.
You can either use methods that will be translated into queries or write your queries in the #Query annotation.
Please read the docs: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories
Let's assume an application where there are leagues and teams inside of leagues, and teams can be in multiple leagues aswell. So we do have a many to many relationship.
League Entity
#Data
#Entity
public class League {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String countryCode;
private SportType sportType;
#ManyToMany(mappedBy = "leagues")
private List<Team> teams;
}
Team Entity
#Data
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "team_league",
joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "league_id", referencedColumnName = "id"))
private List<League> leagues;
}
I am now on the point where i need for example to create a new team, and upon creation, it needs a league to put in, which has to already exist. That means i need an endpoint which takes a list of leagueIds instead of a List<League>. So i assume i need to build a DTO. But how should the DTO look like and how would i implement the method that maps the DTO to an entity and saves it to the database.
My idea of the TeamDTO
#Data
public class TeamDTO {
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
private List<Integer> leagueIds;
}
So instead of a List<League> i do have a List<Integer> leagueIds so that the endpoint can accept proper JSON. Is that correct?
Now i want to create the team in the database, IF the leagues of List<Integer> leagueIds are present in the database. So my question now is, when do i map to the entity.
My idea of the implementation of the service
public class TeamServiceImpl implements TeamService {
#Autowired
private LeagueRepository leagueRepository;
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
List<Long> ids =
teamDTO.getLeagueIds().stream().filter(leagueId ->
leagueRepository.findById(leagueId).isPresent()).
collect(Collectors.toList();
if (!ids.isEmpty()) {
Team team = new Team();
team.setName(teamDTO.getName());
team.setLogoUrl(teamDTO.getLogoUrl());
team.setSportType(teamDTO.getSportType());
// do i actually need the League entities to set this?
team.setLeagues(...);
return team;
}
return null;
}
}
Most important question is: Is this the correct way?
Should i use a mapper for DTO to entity and vice versa?
Should i implement a mapper myself (i mean it only maps a few
fields)?
And on what place i would use the mapper, if i would implement one?
I don't know why you only want to save the team if it has leagues assigned that exist. It just sounds wrong to me i.e. some kind of bug is in your app if the league for an id does not exist. You should set a list of league references and rely on the FK-constraint to error if a wrong league id is used i.e. use something like this:
List<League> leagues =
teamDTO.getLeagueIds().stream().map(leagueId ->
leagueRepository.getOne(leagueId)).
collect(Collectors.toList());
team.setLeagues(leagues);
The DTO approach is fine and as long as it stays this simple, I guess using this custom implementation is good enough. If you have more complex requirements and want to make use of more efficient processing I would recommend you look into Blaze-Persistence Entity-Views which was made for exactly this purpose, efficient mapping between JPA entities and DTOs.
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(Team.class)
#UpdatableEntityView
public interface TeamDTO {
#IdMapping
Long getId();
String getName();
void setName(String name);
String getLogoUrl();
void setLogoUrl(String logoUrl);
SportType getSportType();
void setSportType(SportType sportType);
#UpdatableMapping
#JsonIgnore
List<LeagueDto> getLeagues();
default List<Long> getLeagueIds() {
return getLeagues().stream().map(LeagueDto::getId).collect(toList());
}
default void setLeagueIds(List<Long> ids) {
getLeagues().clear();
ids.stream().map(id -> evm().getReference(LeagueDto.class, id)).forEach(getLeagues()::add);
}
// This is a special context providing method
EntityViewManager evm();
#EntityView(League.class)
interface LeagueDto {
#IdMapping
Long getId();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDTO a = entityViewManager.find(entityManager, TeamDTO.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<TeamDTO> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
The saving part will then be as simple as this:
public class TeamServiceImpl implements TeamService {
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
teamRepository.save(teamDTO);
return teamRepository.getOne(teamDTO.getId());
}
}
Due to the change-tracking implementation of Entity-Views, at any point in time it is clear what is dirty and will by default only flush these changes and avoid unnecessary select statements during flushing.
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!
I have to MySQL tables TICKET-is the parent and USER-is the child, in a many-to-one relationship.
=TICKET=
PK(ID)
summary
message
FK(user_id) references USER(user_id)
=USER=
PK(ID)
email
password
And the JPA entities
#Entity
class TICKET {
#Id
private Integer ID;
private String summary;
private String message;
#ManyToOne
#JoinColumn(name="user")
private USER user;
}
#Entity
class USER {
#Id
private Integer ID;
private String email;
private String password;
}
If i make a query to get a ticket by ID it will return also the user information (USER.ID, USER.email, USER.password) which is not good.
ticketsCrudRepository.findById(ticketId);
What i want is to get a table that looks like this:
TICKET.ID | summary | message | USER.email
I know how to do it in MySQL but JPA it's to much for me. I don't want to use JPQL or native query language.
Any suggestions?
When you fetch a Ticket you also get a User since #ManyToOne by default has FetchType.EAGER. You can change this by changing the anotation to #ManyToOne(fetch = FetchType.LAZY)
To do the search you chould try somethig like
public List<Object[]> getTikcetsForUser(final User user) {
String hql = "select t.id, t.summary, t.message, u.email "
+ "from Tikcet t, User u "
+ "where ticket.user = :user";
Query<Object[]> query = getSession().createQuery(hql, Object[].class);
query.setParameter("user", user);
return query.getResultList();
}
The returned List will contain arrays with 4 fields (t.id, t.summary, t.message, u.email).
Please let me know if you find this useful.
:)
The solution i was looking for was Spring Data JPA Projections. They can be interfaces or classes with getters that match the columns you want to get from the database. For detailed informations check the Spring Framework documentation here.
Using Spring Data Projections is one option, but you will at some point run into the limitations it has. If you reach that point, you can look into 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(TICKET.class)
public interface TicketDto {
#IdMapping
Long getId();
String getName();
UserDto getUser();
#EntityView(USER.class)
interface UserDto {
#IdMapping
Long getId();
String getEmail();
}
}
Or even simpler in your case
#EntityView(TICKET.class)
public interface TicketDto {
#IdMapping
Long getId();
String getName();
#Mapping("user.email")
String getUserEmail();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TicketDto a = entityViewManager.find(entityManager, TicketDto.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
In my project, I am using Spring Data JPA and extend the JpaRepository interface for my data fetching class.
OrganizationMaster class :
#Entity
#Table(name="organization_master")
public class OrganizationMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="organization_id")
private int organizationId;
#OneToMany(mappedBy="organizationMaster")
private List<CompanyMaster> companyMasters;
}
CompanyMaster Class:
Entity
#Table(name="company_master")
public class CompanyMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="company_id")
private int companyId;
#ManyToOne
#JoinColumn(name="organization_id")
private OrganizationMaster organizationMaster;
}
My Controller
#RequestMapping(value = "/GetOrganization", method = RequestMethod.GET)
public
#ResponseBody
List<OrganizationMaster> getOrganization(){
return organizationService.getOrganization();
}
OrganizationService:
public interface OrganizationService {
List<OrganizationMaster> getOrganization();
}
OrganizationServiceImpl:
#Service
public class OrganizationServiceImpl implements OrganizationService{
#Autowired
private OrganizationDao organizationDao;
#Override
public List<OrganizationMaster> getOrganization() {
return organizationDao.findAll();
}
}
OrganizationDao Interface:
public interface OrganizationDao extends JpaRepository<OrganizationMaster,Long> {
}
My Output Response is:
[{"organizationId":5,"companyMasters":[{"companyId":29},{"companyId":30}]}]
But my need is
[{"organizationId":5}]
When I am trying to get data from the organization master using findall() method it also fetches data from the company master based on the relationship. How can I achieve lazy fetching (get data only from organization master) using spring data JpaRepository
All XToOne associations are default EAGER. You have a bidirectional relationship, so you should use FetchType.LAZY on your #ManyToOne side.
#ManyToOne(fetch = FetchType.LAZY)
Also if you use any serializer (like json serializer) when it serialize it calls getter methods and it may causes to load lazy items unneccessarly.
Another consideration is while using Lombok, #Data annotation causes to load lazy items without need. So be careful when using Lombok.
So in your case, beacuse of you return entity itself, while serialization it serializes the child too, it causes to load lazly child entity.
You need to return a dto which represents only your parent entity to prevent serialization of child entity. If you call child with getter method, it laods lazly child entity from database.
Take a look for further information associations:
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
I believe this question is asked before!you can use this annotation:
#OneToMany( fetch = FetchType.LAZY )
read this article for better view in this point:
https://howtodoinjava.com/hibernate/lazy-loading-in-hibernate/