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
Related
I'm using Spring boot 2.7.0
And have the next entities in simple:
#Getter
#Setter
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Long version;
private String name;
}
#Getter
#Setter
#Entity
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
Account account;
private String message;
}
and jpa repositories:
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
}
#Repository
public interface EventRepository extends JpaRepository<Event, Long> {
Page<Event> findAllByAccount(Account account, Pageable pageable);
}
In short I call
eventRepository.findAllByAccount(accountRepository.findById(1), PageRequest.of(1,10));
Problem is every call of last code increases the version field of Account by 1. So question is why? I don't call any update or save method.
And additionally the result of this behaviour is calling of method needs
#Transactional(readonly=false)
Otherwise if I write readonly=true that throws cannot execute UPDATE in a read-only transaction
ADDED:
full code of usage:
#Transactional
public Page<Event> events(Long accountId, int page) {
return eventRepository.findByAccount(findById(accountId), PageRequest.of(page, PAGE_SIZE));
}
#GetMapping("/events")
public List<EventResponse> listEvents(#RequestParam(value = "max", defaultValue = "0") int page) {
return eventService.events(1L, page).stream().map(EventResponse::of).toList();
}
It looks like hibernate is deriving lockMode type as either of WRITE or OPTIMISTIC_FORCE_INCREMENT or PESSIMISTIC_FORCE_INCREMENT based on isolation level of your database. As per reference hibernate decides this pessimistic locking by its own based on database you use.
As per doc, if lockmode type is either of what I mentioned above, Version will get automatically incremented even if you haven't changed anything i.e. even if you haven't do any update or save.
Please check database isolation level & based on that you might get an idea about this.
Edit: as you explicitly setting lockmode as write so my answer validates that because of WRITE mode, your version got incremented automatically.
The problem should be related in the code which is using the result of the find.
If you're modifying entities under a transaction they're going to be modified at the end of the method, when Spring in this case is going to close the transaction. In this part when transaction ends, the JPA provider (for example hibernate) aligns the relative entity record into the database with the 'java entity object' by an update.
I'm sorry. After trim all my code to the posted and debug I found my mistake:
In the begin I was retrieving Account in another method by .lock(Long) method instead of .findById(Long)
lock method is below:
#Lock(LockModeType.WRITE)
#Query("from Account where id = :id")
public Optional<Account> lock(Long id);
I am currently working on a Spring Boot project and I would like to speed up process of writing the service/data layer boilerplate code (one service and one repository (CrudRepository) for every entity, every one having mostly the same methods).
As of now I am using TABLE_PER_CLASS inheritance in several entities (e.g.: Warehouse and Office are subclasses of Location (an abstract class defining common attributes for all locations).
I would like to define 1 repository and 1 service to manage both Location and its subtypes so I can do something like in my control layer:
#Autowired
LocationsService locationsService;
Warehouse cityWarehouse = new Warehouse();
Office centralOffice = new Office();
locationsService.addNewLocation(cityWarehouse);
locationsService.addNewLocation(centralOffice);
I know I can just use method overloading but I would really like to avoid repeating the same code in situations like this one.
I've also tried using parametric polymorphism:
#Service
public class LocationsService {
#Autowired
LocationsRepository locationsRepository;
public void addNewLocation(Location location) {
locationsRepository.save(location);
}
}
Unfortunately this won't work as Spring can't tell if I want to save a Location or a Warehouse object:
nested exception is org.springframework.orm.jpa.JpaObjectRetrievalFailureException:
Unable to find com.test.springboot.entities.locations.Location with id 55db6993-8a58-4e3a-a6ab-d60d93ab6182; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.test.springboot.entities.locations.Location with id 55db6993-8a58-4e3a-a6ab-d60d93ab6182
I need to use concrete Location objects so using #MappedSuperclass is not an option.
Is there something I am missing? Is it even posible to achieve what I want?
Please note that I am fairly new to Spring Boot so maybe there's something obvious I don't know about yet.
I got it working thanks to some of the comments and after briefly reading the JPA specification.
Because I wanted to use my superclasses as entities I ended up using SINGLE_TABLE inheritance.
For example, this is the Location entity:
#Entity
#Data
#Accessors(chain = true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "location_type")
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// Skipped
}
The key here is to use #DiscriminatorColumn with SINGLE_TABLE inheritance in the parent class and add #DiscriminatorValue in the correspondent subclasses:
#Entity
#Data #Accessors(chain = true)
#DiscriminatorValue("warehouse_location")
public class Warehouse extends Location {
#JoinColumn(name = "INTERNAL_ROUTE_ID")
#OneToOne(orphanRemoval = true)
private Route internalRoute;
#JoinColumn(name = "EXTERNAL_WAREHOUSE_ID")
#OneToMany(orphanRemoval = true, fetch = FetchType.EAGER)
private List<Warehouse> externalWarehouses;
}
This way, I can define LocationsRepository as:
public interface LocationsRepository extends CrudRepository<Location, Long> {
Warehouse findByCityIgnoreCase(String city);
}
Also note that subclass-specific methods can be defined here as long as its return type is explicitly specified (otherwise the method would return ALL Locations, not just the Warehouses).
Finally, in the service layer I can make the relevant methods return any entity just by downcasting the result of the repository call into the appropriate subclass
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 a requirement in my project that all my methods should be either abstract or final (please don't argue this requirement -- I know it's dumb, just assume it's there).
This is a problem with Hibernate mapped entities since Hibernate needs to create proxies in run-time in order to be able to initialize relations when those are lazily loaded. Not being able to override setter methods results in those not being loaded at all (query is indeed executed, but the object is never populated).
As stated in Hibernate's documentation:
If the final class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See Example 4.4, “Proxying an interface in hbm.xml” and Example 4.5, “Proxying an interface in annotations”.
Example:
#Entity #Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
So theoretically it's possible to just tell hibernate to implement an interface instead of extending the original class.
I've tried this solution, but my problem comes with the relations themselves. Here's an over-simplified example:
#Entity
#Proxy(proxyClass = ICat.class)
#Table(name = "cat")
public class Cat implements ICat {
#Id
private Long catId;
#OneToMany(mappedBy = "cat", fetch = FetchType.LAZY)
private List<Kitten> kittens;
...
}
#Entity
#Proxy(proxyClass = IKitten.class)
#Table(name="kitten")
public class Cat implements IKitten {
#Id
private Long kittenId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="catId")
private Cat cat;
...
}
Now if I try to obtain a Cat object, I get a ClassCastException since it is trying to cast an IKitten collection into a Kitten collection. Which leads me to think I should declare relations using interfaces instead of implementations -- which also produces a compilation-time error since my Interfaces are never declared as entities, but the implementations are (which is clearly stated in the example from the documentation).
How can I solve this?
You con use the interface in both one-to-many and many-to-one associations, but you need to supply the actual Class in the targetEntity attribute. The relations should be something like this:
#Entity
#Proxy(proxyClass = ICat.class)
#Table(name = "cat")
public class Cat implements ICat {
#Id
private Long catId;
#OneToMany(mappedBy = "cat", fetch = FetchType.LAZY, targetEntity=Cat.class)
private List<IKitten> kittens;
...
}
#Entity
#Proxy(proxyClass = IKitten.class)
#Table(name="kitten")
public class Cat implements IKitten {
#Id
private Long kittenId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="catId", targetEntity=Cat.class)
private ICat cat;
...
}
I had the same requirement before, and bypassed it by declaring final all classes. My hibernate structure was not proxying, and thus I don't know if that will fix the problem, but the requirement. There is a side problem if you are using mockito. Have this in mind.
By the way, there is a typo in the second class shown in your code, it shall be named Kitten
Is there any possibility with Hibernate to do the following entity structure?
#Entity
public class Person {
#OneToMany
private Map<Class<? extends PersonRole>, PersonRole> personRoles;
public <T extends PersonRole> T getRole(Class<T> roleClass) {
return roleClass.cast(roles.get(roleClass));
}
}
#Entity
public abstract class PersonRole {
#ManyToOne
private Person person;
}
Basically Hibernate can persist this mapped entity but it is not possible to load it anymore from the database with the following exception:
Exception in thread "main" org.hibernate.HibernateException: null index column for collection: de.his.cs.sys.hibernate.Person.roles
at org.hibernate.persister.collection.AbstractCollectionPersister.readIndex(AbstractCollectionPersister.java:822)
at org.hibernate.collection.internal.PersistentMap.readFrom(PersistentMap.java:277)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1189)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:804)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:655)
at org.hibernate.loader.Loader.doQuery(Loader.java:854)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:293)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:263)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2094)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
A workaround could be using a "simple" collection and filling the map with an interceptor, but I hope for a possibility achieving this without additional infrastructure.
it is possible implementingh a Hibernate UserType which maps the class to a string and back
#OneToMany
#MapKey(name = "className" type=#Type(type="namespace.classToNameUserType"))
private Map<Class<? extends PersonRole>, PersonRole> personRoles;
see here for an example UserType
The problem basically seems to me, that hibernate needs to rely on a persistent attribute for a map key. Therefore the solution adds a new attribute to the abstract class RersonRole:
private Class<?> className = this.getClass();
Then it is possible to refer to it in the #MapKey annotation in the class Person:
#OneToMany
#MapKey(name = "className")
private Map<Class<? extends PersonRole>, PersonRole> personRoles;
With this mapping hibernate can now fill the Map without further infrastructure.
This from my point of view mostly elegant solution has the drawback of adding a persistent attribute, which is only needed because of hibernate (If I get the root cause of the problem right).