I have the Problem that a quite simple select (SELECT h FROM Hero h where h.owner = :player) causes a n+1 Problem. The query triggers the select to the game for each hero entity.
First i added fetch type lazy which removed the alternative queries during the select. In my code i need the game only once to get the id. This triggered the select again for every entity.
So i added a join fetch which helped, BUT it causes a join i don't need! How can i tell the hibernate proxy object to give me the id without fetching the whole entity?
I tried the tips there without success: http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml
Both #AccessType from hibernate and #Access from javax make no difference. The game will always be fetched when i call getId().
Any Ideas what i am missing? I would need this quite often and i would like to avoid joins if i can.
greetings,
markus
PS: I use jpa2 + hibernate 4 with jboss 7.
#Entity
#NamedQueries({
#NamedQuery(name = "Hero.findByPlayer", query = "SELECT h FROM Hero h JOIN FETCH h.game where h.owner = :player")
})
public class Hero extends GameCharacter implements Serializable {
#ManyToOne
private Player owner;
#Entity
public abstract class GameCharacter extends GameObject implements Serializable {
... nothing special in here
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class GameObject implements Serializable {
#Id
private String id = java.util.UUID.randomUUID().toString();
#ManyToOne(fetch=FetchType.LAZY)
protected Game game;
#Entity
public class Game implements Serializable {
private static final long serialVersionUID = 4379242677193301727L;
#Id
private String id = java.util.UUID.randomUUID().toString();
This is just a side affect of the way lazy loading in hibernate works. Essentially, any access of the lazy object will cause it to populate. It doesn't make a special case of the id.
I don't think you're actually going to be able to access the ID from the game object without triggering the loading. What you can do is ask hibernate for the id of an object using the getIdentifier method. I believe this will not trigger the fetch as hibernate is simply looking up the metadata it has for the object.
Alternatively you can load the game id alongsite the hero when executing your query.
E.g.
SELECT h, h.game.id FROM Hero h where h.owner = :player
Rather than finding you a list of heros as your current query does, this will return a list of pairs, each pair having the hero and the game id.
ID is treated specially in HQL so this shouldn't trigger the fetch or join you're getting when trying to access the object.
Related
First, here are my entities.
Player :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
#ManyToOne
#JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
Team :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
#OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
As many topics already stated, you can avoid the StackOverflowExeption in your WebService in many ways with Jackson.
That's cool and all but JPA still constructs an entity with infinite recursion to another entity before the serialization. This is just ugly ans the request takes much longer. Check this screenshot : IntelliJ debugger
Is there a way to fix it ? Knowing that I want different results depending on the endpoint. Examples :
endpoint /teams/{id} => Team={id..., members=[Player={id..., team=null}]}
endpoint /members/{id} => Player={id..., team={id..., members=null}}
Thank you!
EDIT : maybe the question isn't very clear giving the answers I get so I'll try to be more precise.
I know that it is possible to prevent the infinite recursion either with Jackson (#JSONIgnore, #JsonManagedReference/#JSONBackReference etc.) or by doing some mapping into DTO. The problem I still see is this : both of the above are post-query processing. The object that Spring JPA returns will still be (for example) a Team, containing a list of players, containing a team, containing a list of players, etc. etc.
I would like to know if there is a way to tell JPA or the repository (or anything) to not bind entities within entities over and over again?
Here is how I handle this problem in my projects.
I used the concept of data transfer objects, implemented in two version: a full object and a light object.
I define a object containing the referenced entities as List as Dto (data transfer object that only holds serializable values) and I define a object without the referenced entities as Info.
A Info object only hold information about the very entity itself and not about relations.
Now when I deliver a Dto object over a REST API, I simply put Info objects for the references.
Let's assume I deliever a PlayerDto over GET /players/1:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
Whereas the TeamInfo object looks like
public class TeamInfo {
private String teamName;
private String teamColor;
}
compared to a TeamDto
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
This avoids an endless serialization and also makes a logical end for your rest resources as other wise you should be able to GET /player/1/team/player/1/team
Additionally, the concept clearly separates the data layer from the client layer (in this case the REST API), as you don't pass the actually entity object to the interface. For this, you convert the actual entity inside your service layer to a Dto or Info. I use http://modelmapper.org/ for this, as it's super easy (one short method call).
Also I fetch all referenced entities lazily. My service method which gets the entity and converts it to the Dto there for runs inside of a transaction scope, which is good practice anyway.
Lazy fetching
To tell JPA to fetch a entity lazily, simply modify your relationship annotation by defining the fetch type. The default value for this is fetch = FetchType.EAGER which in your situation is problematic. That is why you should change it to fetch = FetchType.LAZY
public class TeamEntity {
#OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
Likewise the Player
public class PlayerEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
When calling your repository method from your service layer, it is important, that this is happening within a #Transactional scope, otherwise, you won't be able to get the lazily referenced entity. Which would look like this:
#Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
In my case I realized I did not need a bidirectional (One To Many-Many To One) relationship.
This fixed my issue:
// Team Class:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// #ManyToOne
// #JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok might also produce this issue. Try adding #ToString and #EqualsAndHashCode if you are using Lombok.
#Data
#Entity
#EqualsAndHashCode(exclude = { "members"}) // This,
#ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
This is a nice guide on infinite recursion annotations https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can use #JsonIgnoreProperties annotation to avoid infinite loop, like this:
#JsonIgnoreProperties("members")
private Team team;
or like this:
#JsonIgnoreProperties("team")
private List<Player> members;
or both.
I have some entities like this (a post with some tags associated with):
#Entity
public class Post {
#GeneratedId
public Long id;
#ManyToMany(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
public Set<Tag> tags = new HashSet<Tag>();
...
}
#Entity
public class Tag {
#GeneratedId
public Long id;
public String name;
}
I have to make some requests that returns hundreds of Posts with filtering on tags.
Up to now, I have used standard queries returning some Post entities, then accessing the myPost.tags to create my POJO.
This is really time consuming because each time I create a POJO, I call myPost.tags which makes a request to get them. I thought it was a good idea to use the select new feature like this:
select distinct new PostDTO(p.id, t) from Post p left outer join p.tags as t where ...
with
public class PostDTO {
public PostDTO(Long id, Tag[] tags) {...}
}
Unfortunately, due to JPQL: Receiving a Collection in a Constructor Expression the tags could not be passed as an array.
So my question: how can I do? Do I have to change my schema? or JPA has a feature that I've missed?
Having trouble to create the right itnerface for my Query for this given Problem.
I have this entity:
public class TwoEntity extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/*
* ATTRIBUTE
*/
private String groupName;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<OneEntity> oneList;
And the Crud Repositoy:
public interface TwoRepository extends CrudRepository<TwoEntity, Long> {
TwoEntityfindById(Long id);
TwoEntityfindByGroupName(String groupName);
List<TwoEntity> findBy????(OneEntity oe);
My Goal is to get All TwoEntities where OneEntitie is a element in the list of TwoEntity.
I´am using Spring boot and Hibernate to accomplish this. I cant delete the OneEntity Object from my Database because TwoEntity has OneEntity as ForeignKey in the List.
Is there anyway of get this to work with the given Tools from the Interface?
A List of available KeyWords can be found here: spring docs for crud
/E
I Guess I have a wrong Architecture. Currently I have a Unidirectional Relation between this Entities. I guess I have to make those entities bidirectional and delete them manuelle with oneList.setList(null).
BUT I´m not 100% sure. Open for Input.
You could use this:
List<TwoEntity> findByOneList_Id(Long oneEntityId)
But you need to extend from JpaRepository
public interface TwoRepository extends JpaRepository<TwoEntity, Long> {
The method will be translated to
twoEntity.oneList.id
Here official doc
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
The situation is as following. Say I've got a Kid, and I want to get his Parent. The abstract Parent is situated in a View. The Parent can either be a Mother or a Father. The Mother is also defined in a View.
#Entity
public class Kid extends DomainObject {
private IParent theParent;
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Parent.class)
#JoinColumn(name = "TheParentId")
public IParent getTheParent() {
return theParent;
}
#Override
public void setTheParent(IParent theParent) {
this.theParent = theParent;
}
}
#Entity
#Inheritance(strategy= InheritanceType.JOINED)
#Table(name = "Parent")
public abstract class Parent {
}
#Entity
public class Mother extends Parent {
}
So, using Hibernate, I try to retrieve the Mother-object by calling getTheParent() on Kid:
from Kid hobj left join fetch hobj.theParent pa
This works fine in the database. But when running this via Hibernate, it returns SQL exception: Invalid column name(UNKNOWN)
There are a few pointers which can be the cause. In the old database, Mother and Father were Table's instead of Views. My guess is that Hibernate understood the link because of foreign keys. Changing the Parent from Table to View broke the application. But searching around points out that Hibernate treats View's and Table's equally (to some extent). Why does getting the exact query being used by Hibernate work when entered directly, but not when using Hibernate?
The problem seemed to be in the select-clause of the query. When adding multiple objects to the select column (running deeper with more tables then Kid, Mother and Father), Hibernate would get confused. When I only used the Kid-object (and fill up items using Kid.getX().getY().getZ()... ), Hibernate had no problem fetching everything.
Seems to be a bug in Hibernate.
I have a Client and Affiliate class, inheriting from Person class. Joined inheritance strategy type is being used - each of them sharing primary key with the parent class. As there's no discriminator column we chose to use DescriptorCustomizer and ClassExtractor. But it doesn't really give any idea how it works, also, the code doesnt seem to compile. It would be nice if someone gives a nice example with code snippet for understanding.
According to the mentioned documentation:
If you are mapping to an existing database, and the tables do not have
a discriminator column you can still define inheritance using the
#ClassExtractor annotation or <class-extractor> element. The class
extractor takes a class that implements the ClassExtractor
interface. An instance of this class is used to determine the class
type to use for a database row. The class extractor must define a
extractClassFromRow() method that takes the database Record and
Session.
we need to annotate the root entity in a hierarchy with user defined using the class extractor:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#ClassExtractor(PersonClassExtractor.class)
public abstract class Person {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private int age;
// ...
}
Notice that we don't use #Customizer annotations since as this is not required in case of JOINED inheritance strategy:
If a class extractor is used with SINGLE_TABLE inheritance, the rows
of the class type must be able to be filtered in queries. This can be
accomplished by setting an onlyInstancesExpression() or
withAllSubclassesExpression() for branch classes. These can be set
to Expression objects using a DescriptorCustomizer.
The class extractor must be able to determine and return the class type from the database row.
In general we need a replacement of a discriminator column, i.e.
column name unique for a given entity type among others
criteria based on values of a given column of the root entity
Suppose that each of inherited entity type in a hierarchy has a column with unique name:
#Entity
public class Client extends Person {
#Column(name = "CLIENT_SPECIFIC")
private String clientSpecific;
// ...
}
#Entity
public class Affiliate extends Person {
#Column(name = "AFFILIATE_SPECIFIC")
private float affiliateSpecific;
// ...
}
then class extractor may look as follows:
public class PersonClassExtractor extends ClassExtractor {
#Override
public Class<?> extractClassFromRow(Record databaseRow, Session session) {
if (databaseRow.containsKey("CLIENT_SPECIFIC")) {
return Client.class;
} else if (databaseRow.containsKey("AFFILIATE_SPECIFIC")) {
return Affiliate.class;
} else {
return Person.class; // this should never happen
}
}
}
retrieve a list of clients and affiliates
List<Person> polymorphicResults = em.createQuery("SELECT p FROM Person p")
.getResultList();
retrieve a list of affiliates or clients respectively
List<Affiliate> concreteResults = em.createQuery("SELECT a FROM Affiliate a")
.getResultList();
List<Client> concreteResults = em.createQuery("SELECT c FROM Client c")
.getResultList();