JPA query -- I don't want it through #Query - java

Custom Project interface for selecting only required fields of main table and reference entities.
public interface SimpleProjection{
Long getId();
interface Location{
Long getId();
}
interface Address{
String getCity();
}
}
Entities
#Entity
public class Simple{
#Id
private Long id;
#OneToOne
Location mainLocation;
#OneToOne
Location tempLocation;
}
#Entity
public class Location{
#Id
private Long id;
private String name;
//many more
}
Spring Repository
public interface SimpleRepository extends JpaRepository<Simple, Long> {
Optional<SimpleProjection> getById(Long id);
}
While I invoking getById() method on repository, it fetches all the column of related entities instead of defined one.
Please feel free to suggest better approach for projection as I have many related entites when I used simpler approach without projection around 200 columns get fetched that's why I am using Projection approach.

Related

How should a DTO look like compared to an Entity

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.

Hibernate: How to map a class that "has-a" subclass to a single table?

I have a class BacktestResult that contains a property BacktestInputs. I want hibernate to create a single table that contains the contents of both. Here's what I have so far:
#Entity
#Table(name="backtest_result")
public class BacktestResult {
#Id
Long backtest_id;
public int userId;
BacktestInputs inputs;
#OneToMany
#OrderColumn(name="backtest_id")
public BacktestChartDataPoint[] series;
//getters and setters
The BacktestChartDataPoint class looks like this:
#Entity
#Table(name="backtestchart_datapoint")
public class BacktestChartDataPoint {
#Id
public Long id;
public Long backtest_id;
#ManyToOne
#JoinColumn(name="backtest_id", nullable=false, insertable=false, updatable=false)
private BacktestResult backtestResult;
The BacktestInputs class looks like this:
public class BacktestInputs {
public String strategy_name;
public String version;
etc...
As long as I comment out the BacktestInputs in BacktestResult, hibernate correctly creates 3 tables:
backtest_result
backtest_result_series
backtestchart_datapoint
But I want the contents of BacktestInputs to be included in the BacktestResult table. If I uncomment it, get error:
Could not determine type for:
net.tekknow.moneymachine.model.BacktestInputs, at table:
backtest_result, for columns: [org.hibernate.mapping.Column(inputs)]
What is the proper way to handle class hierarchy with "has-a" relationships, NOT "is-a" relationships?
You need to annotate the class BacktestInputs with #Embeddable
(see https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#embeddables)

JPA - ManyToOne: Join columns and query tables without using whole Object

I have an OrderItemEntity that I want to associate to a specific OrderEntity in a ManyToOne relationship (many OrderItems belong to one order).
I want to join the columns of both tables with #JoinColumn, but without using the (Java-Object) OrderEntity.
Reason: Whenever I want to query the OrderItemRepository (e.g. list all OrderItems to a specific order) I have to supply an OrderEntity (findByOrderId(OrderEntity orderEntity)), but instead, I'd like to supply a simple OrderId. Is there a better way to accomplish that?
My OrderItemEntity Class:
#Entity
#Data
#AllArgsConstructor
public class OrderItemEntity {
#Id
private String orderItemId;
private int quantity;
private double subtotal;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "orderId")
private OrderEntity orderId;
public OrderItemEntity() {
}
}
Repository Interface:
#Repository
public interface OrderItemEntityRepository extends CrudRepository<OrderItemEntity, String>{
ArrayList<OrderItem> findByOrderId(OrderEntity orderEntity);
}
Method in Service-Class to query the repository
#Override
public ArrayList<OrderItem> getOrderItems(String orderId) {
return orderItemEntityRepository.findByOrderId(new OrderEntity(orderId, null, null));
}
First change the property to private OrderEntity order; and change the method in the repository to ArrayList<OrderItem> findByOrder_Id(Long orderEntityId);, then you just pass the order entity id to get the entities.

How do I retrieve only the ID instead of the Entity of an association?

I have a class that looks something like this:
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
and another one that looks something like this:
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne
public EdgeInnovation replacedEdge;
}
and so each table map to the other, so one entity will refer to other entities that will refer to more entities and so on, so that in the end there will be many entities that will be fetched from the database. Is there any way to only get the value (integer/long) of the key and not the entity it refers to? something like this:
#ManyToOne(referToThisTable="NodeInnovation")
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne(referToTable="NodeInnovation")
public Long destination;
#ManyToOne(referToTable="NodeInnovation")
public Long origin;
}
and
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne(referToTable="EdgeInnovation")
public Long replacedEdge;
}
Here's an example. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.
You would just map the foreign keys as basic mappings instead of Relationships:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID")
public Long destination;
#Column(name="ORIGIN_ID")
public Long origin;
}
Or you can have access to both the ID and the referenced entity within EdgeInnovation, but you'll need to decide which you want to use to set the mapping:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID", updatable=false, insertable=false)
public Long destination_id;
#ManyToOne
public NodeInnovation destination;
#Column(name="ORIGIN_ID", updatable=false, insertable=false)
public Long origin_id;
#ManyToOne
public NodeInnovation origin;
}
In the above example, the origin_id is read-only while the origin reference is used to set the foreign key in the table. Any changes though should be made to both fields to keep the object mappings in synch with each other.
Another alternative is to use the provider's native code to find if the reference is lazy and wasn't triggered, and then get the foreign key value. If it has been triggered, you can just use the reference to get the ID value, since it won't cause a query to fetch anything. This is something you would have to look into EclipseLink's source code for though.
Sorry, I cant comment so I put it here ,
I think it should be like that
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
And the other class is :
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToMany(mappedBy="origin")
public List<EdgeInnovation> replacedEdges;
}
If I'm getting the situation wrong sorry, (Could you draw your classes with the relations so I can get it straight?)
Why not use a new construction in JPA and a custom constructor in NodeInnovation? Basically, create a transient property in NodeInnovation for use when you only want the EdgeInnovation id:
#Entity
public class NodeInnovation {
#Id #GeneratedValue private Long id;
private Integer type;
#OneToOne
private EdgeInnovation replacedEdge;
#Transient
private Long replacedEdgeId;
public NodeInnovation() {}
public NodeInnovation(Long id, Integer type, Long replacedEdgeId ) {
this.id = id;
this.type = type;
this.replacedEdgeId = replacedEdgeId;
}
...
}
Use it like so:
NodeInnovation n = em.createQuery("select new NodeInnovation(n.id, n.type, n.replacedEdge.id) from NodeInnovation n where n.id = 20", NodeInnovation.class).getSingleResult();
You didn't say how you were selecting NodeInnovation, whether directly or through a join, but either way the trick is the new NodeInnovation in the JPQL or CriteriaBuilder query.
I am aware I am quite late but some people might look for an answer to the same question - in your JPA repository you could do something like this:
#Query("SELECT new java.lang.Integer(model.id) FROM #{#entityName} model WHERE model.relationModeFieldlName.id IN :relationModelIds")
List<Integer> findIdByRelationModelIdIn(#Param("relationModelIds") List<Long> relationModelIds);

Is there a way (e.g. an Eclipse plugin) to automatically generate a DTO from an Entity (JPA)?

I would like a plain forward DTO generation tool that would either
Generate it on the fly (e.g. cglib - create the class and DTO object on the fly)
Or an Eclipse plugin that will take the Entity and generate a DTO (user will specify which tree graph to include, and for non included, will include foreign keys instead of related entities etc)
E.g. take something like this
#Entity
#Table(name="my_entity")
public class MyEntity {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private RelatedEntity related;
public RelatedEntity getRelated(){
return related;
}
...
And generate something like this :
#Entity
#Table(name="my_entity")
public class MyEntity imlpements MyEntityDTO {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private RelatedEntity related;
//overrides MyEntity interface, it's allowed to narrow return type
public RelatedEntity getRelated(){
return related;
}
...
//implements MYEntityDTO respective interfaces
public Long getRelatedId(){return related.getId();}
And DTO interface(s):
public interface MyEntityDTO {
public String getId();
public String getName();
public Long getRelatedId();
public RelatedEntityDTO getRelated(); //RelatedEntity implements RelatedEntityDTO
...
}
public interface RelatedEntityDTO {
...
}
If we don't want to include children in the graph, remove it from the DTO interface:
public interface MyEntityDTO {
public String getId();
public String getName();
public Long getRelatedId();
...
I'm sure there is some eclipse plugn for it and if not, I challange someone to write one, or explain why what I want is not helpful (and provide an alternative suggestion)
Probably Hibernate Tools should be doing this: http://hibernate.org/subprojects/tools.html
Telosys Tools can generate both : JPA entity and DTO
Let's have a look at this tutorial https://sites.google.com/site/telosystutorial/springmvc-jpa-springdatajpa
it generates a full Spring MVC CRUD application with JPA
Architecture : https://sites.google.com/site/telosystutorial/springmvc-jpa-springdatajpa/presentation/architecture
The mapper Entity/DTO is also generated (it uses "org.modelmapper" )
The templates are customizable
Try to look at:
https://github.com/nikelin/spring-data-generation-kit
But it's only suitable for you if your project is under the
Maven control.

Categories