My team made a decision not to remove entries from database, but to give them a status like ACTIVE or DELETED.
Problem arises when, for entity - let's call it Customer I make a collection of another entities:
#Entity
public class Customer {
#ManyToMany
private List<Order> orders;
private MyEnumStatus status;
...
}
If I want to 'remove' one order from the list orders I have no influence on how is it done (especially - it will certainly not set desired status on entry in the link table and instead it will just remove a record from it).
#PersistenceContext
EntityManager em;
...
Customer customer = customerService.getRandomOne();
customer.getOrders().remove(0);
em.merge(customer);
My question is - is it possible to apply a status field (in Order entity) to this scenario? I mean to somehow overwrite this behaviour to set statuses instead of removing entries.
Yes it is.
You can use either Events or Interceptors. Both are described in chapter 14 of the reference docs. This option is powerful but hairy. Play with it at your own risk.
Soft deletes are probably a better option. #mdatwood's link shows one way of doing this. There's a fuller example at feraturenotbug. Don't forget to tweak all your queries return only objects that aren't deleted. That's mentioned in the example.
You need to implement logical deletes. See this answer for more information.
You can override the remove() method implementation using the #SQLDelete annotation as below in your customer entity:
#Entity
#SQLDelete(sql="UPDATE customer SET status = 'deleted' WHERE id = ?")
public class Customer {
#ManyToMany
private List<Order> orders;
}
Related
What's a workaround if I have a relation OneToMany and would like to access the collection that is lazy loaded? Currently I get LazyInitializationException having this:
Club entity:
#Entity
public class Club {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "club", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JsonBackReference
private List<Player> players;
Player entity:
Is it a good idea to have two methods, where one fetches data without players and the second one that fetches also players?
#Override
List<Club> findAll();
#Query("Select clubs from Club clubs left join fetch clubs.players")
List<Club> getAllClubsWithPlayers();
What I'm thinking of is that it is a bad idea, because if I have a situation where I have for example 4 properties that are lazy loaded and I'd need at once 3 of them, I'd have to have a query like: getAllClubsWithPlayersAndSponsorsAndCoaches, so I'd have to have a lot of combinations of such queries.
I don't want to use EAGER, so could you tell me if it's a common way to do this if I need access to players from the Club sometimes which is undoable with the findAll() method that throws LazyInitializationException?
Don't get me wrong - I know where the LazyInitializationException comes from, but I just don't know what's the best way to get access to players if I sometimes need them when fetching clubs. Is my way of doing it correct?
There are 3 choices:
Access all the lazy fields inside a #Transactional method. You don't show your code, but there's usually a Service Facade layer which is responsible for being #Transactional. It invokes Repositories.
Write a query that fetches all the required data. Then you'd have to create a method specifically to fetch all the lazy fields required for that logic.
Use OpenSessionInViewFilter or OpenSessionInViewInterceptor so that Session/EntityManager are started before the execution even reaches the Controller. The Session then would be closed by the same high-level layer at the end of the request processing.
In addition to what Stanislav wrote, I'd like to elaborate on his 2nd point, because I think that this is often the best approach - that's simply because it saves unnecessary calls to the database which results in better performance.
Apart from writing separate JPQL query in your repository for each use-case, you could do one of the following .:
Make your repository extend JpaSpecificationExecutor and programmatically describe what needs to be fetched as described in this answer
Use Entity Graph either described using annotations, or programmatically, and fetch your entities using EntityManager as described in this tutorial
To optionally load what you want you can use EntityGraph.
Declare #NamedEntityGraph at your entity
#Entity
#NamedEntityGraph(name = "Club.players",
attributeNodes = #NamedAttributeNode("players")
)
public class Club {
Then you should annotate your findAll() method with this graph using it's name
#EntityGraph(value = "Club.players")
List<Club> findAll();
However that would override your basic findAll() method.
To avoid this (to have both implementations) you can follow this way:
Add dependency from https://mvnrepository.com/artifact/com.cosium.spring.data/spring-data-jpa-entity-graph/<version>
Then replace your repository with
#Repository
public interface ClubRepository extends JpaSpecificationExecutor<Club>, JpaRepository<Club, Long>, EntityGraphJpaSpecificationExecutor<Club> {
}
And then you'll have basic method findAll() and also from your sevice you can call
List<Club> clubs = clubRepository.findAll(specification, new NamedEntityGraph(EntityGraphType.FETCH, "Club.players"))
Working with Spring Data REST, if you have a OneToMany or ManyToOne relationship, the PUT operation returns 200 on the "non-owning" entity but does not actually persist the joined resource.
Example Entities:
#Entity(name = 'author')
#ToString
class AuthorEntity implements Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
String fullName
#ManyToMany(mappedBy = 'authors')
Set<BookEntity> books
}
#Entity(name = 'book')
#EqualsAndHashCode
class BookEntity implements Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
#Column(nullable = false)
String title
#Column(nullable = false)
String isbn
#Column(nullable = false)
String publisher
#ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
Set<AuthorEntity> authors
}
If you back them with a PagingAndSortingRepository, you can GET a Book, follow the authors link on the book and do a PUT with the URI of a author to associate with. You cannot go the other way.
If you do a GET on an Author and do a PUT on its books link, the response returns 200, but the relationship is never persisted.
Is this the expected behavior?
tl;dr
The key to that is not so much anything in Spring Data REST - as you can easily get it to work in your scenario - but making sure that your model keeps both ends of the association in sync.
The problem
The problem you see here arises from the fact that Spring Data REST basically modifies the books property of your AuthorEntity. That itself doesn't reflect this update in the authors property of the BookEntity. This has to be worked around manually, which is not a constraint that Spring Data REST makes up but the way that JPA works in general. You will be able to reproduce the erroneous behavior by simply invoking setters manually and trying to persist the result.
How to solve this?
If removing the bi-directional association is not an option (see below on why I'd recommend this) the only way to make this work is to make sure changes to the association are reflected on both sides. Usually people take care of this by manually adding the author to the BookEntity when a book is added:
class AuthorEntity {
void add(BookEntity book) {
this.books.add(book);
if (!book.getAuthors().contains(this)) {
book.add(this);
}
}
}
The additional if clause would've to be added on the BookEntity side as well if you want to make sure that changes from the other side are propagated, too. The if is basically required as otherwise the two methods would constantly call themselves.
Spring Data REST, by default uses field access so that theres actually no method that you can put this logic into. One option would be to switch to property access and put the logic into the setters. Another option is to use a method annotated with #PreUpdate/#PrePersist that iterates over the entities and makes sure the modifications are reflected on both sides.
Removing the root cause of the issue
As you can see, this adds quite a lot of complexity to the domain model. As I joked on Twitter yesterday:
#1 rule of bi-directional associations: don't use them… :)
It usually simplifies the matter if you try not to use bi-directional relationship whenever possible and rather fall back to a repository to obtain all the entities that make up the backside of the association.
A good heuristics to determine which side to cut is to think about which side of the association is really core and crucial to the domain you're modeling. In your case I'd argue that it's perfectly fine for an author to exist with no books written by her. On the flip side, a book without an author doesn't make too much sense at all. So I'd keep the authors property in BookEntity but introduce the following method on the BookRepository:
interface BookRepository extends Repository<Book, Long> {
List<Book> findByAuthor(Author author);
}
Yes, that requires all clients that previously could just have invoked author.getBooks() to now work with a repository. But on the positive side you've removed all the cruft from your domain objects and created a clear dependency direction from book to author along the way. Books depend on authors but not the other way round.
I faced a similar problem, while sending my POJO(containing bi-directional mapping #OneToMany and #ManyToOne) as JSON via REST api, the data was persisted in both the parent and child entities but the foreign key relation was not established. This happens because bidirectional associations need to be manually maintained.
JPA provides an annotation #PrePersist which can be used to make sure that the method annotated with it is executed before the entity is persisted. Since, JPA first inserts the parent entity to the database followed by the child entity, I included a method annotated with #PrePersist which would iterate through the list of child entities and manually set the parent entity to it.
In your case it would be something like this:
class AuthorEntitiy {
#PrePersist
public void populateBooks {
for(BookEntity book : books)
book.addToAuthorList(this);
}
}
class BookEntity {
#PrePersist
public void populateAuthors {
for(AuthorEntity author : authors)
author.addToBookList(this);
}
}
After this you might get an infinite recursion error, to avoid that annotate your parent class with #JsonManagedReference and your child class with #JsonBackReference. This solution worked for me, hopefully it will work for you too.
This link has a very good tutorial on how you can navigate the recursion problem:Bidirectional Relationships
I was able to use #JsonManagedReference and #JsonBackReference and it worked like a charm
I believe one can also utilize #RepositoryEventHandler by adding a #BeforeLinkSave handler to cross link the bidirectional relation between entities. This seems to be working for me.
#Component
#RepositoryEventHandler
public class BiDirectionalLinkHandler {
#HandleBeforeLinkSave
public void crossLink(Author author, Collection<Books> books) {
for (Book b : books) {
b.setAuthor(author);
}
}
}
Note: #HandlBeforeLinkSave is called based on the first parameter, if you have multiple relations in your equivalent of an Author class, the second param should be Object and you will need to test within the method for the different relation types.
I want to remove an item from a list in an Entity. I have this Entity :
#Entity
public class PairingCommit extends Model
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "commit")
public List<CommitItem> items;
}
I do the following for removing an Item :
commit.items.remove(item);
commit.update();
But it doesn't remove the object from the database.
I suppose i missed something up...
EDIT : After some search, I'm not sure to use JPA... I'm working with Play framework 2 that use Ebean... But it seems that I have access to JPA annotation..
My first problem was that trying to directly delete the item like this :
CommitItem.byId(id).delete();
But it give a OptimisticLockException.
You should call EntityManager 's remove method on item.
EntityManager em;
item = em.merge(item); // Now item is attached
em.find(PairingCommit.class, [Pairing Commit PK]).items.remove(item);
em.remove(item);
Take a look at this question/answer. The CascadeType annotation will propagate EntityManager operations to the linked entities. The way your code is currently set up, calling
entityManager.remove(pairingCommit);
would also delete all of the CommitItems that the PairingCommit is linked to, but
commit.items.remove(item);
is not an EntityManager operation, so nothing gets propagated.
You can get rid of the linked items directly with the EntityManager.
The specification says:
It is particularly important to ensure that changes to the inverse
side of a relationship result in appropriate updates on the owning
side, so as to ensure the changes are not lost when they are
synchronized to the database.
So, you must remove from the owning side of relation:
commitItem.setCommit(null);
Ok so I have solved the problem of Optimistick Lock. It was that mysql failed to compare floating point number. I have passed to DECIMAL type and works fine now.
But I d'ont understand why the removing of list don't works.
Here an article on how Optimistick lock works : http://www.avaje.org/occ.html
New to Hibernate.
I have User Group many to many relation.
Three tables : User , Group and UserGroup mapping table.
Entities:
#Entity
#Table(name = "user")
public class User {
#Id
#Column (name = "username")
private String userName;
#Column (name = "password", nullable = false)
private String password;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="usergroup",
joinColumns={#JoinColumn(name="username")},
inverseJoinColumns={#JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
#Entity
#Table(name = "group")
public class Group {
#Id
#Column(name = "groupname")
private String groupName;
#Column(name = "admin", nullable = false)
private String admin;
#ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
Notice that in Group Entity I'm using fetch method EAGER.
Now, when I'm calling my DAO to retrive all the groups in the system
using the following criteria:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
I'm getting all the rows from the mappgin table (usergroup) instead of getting the actual number of groups...
for example if i have
in user table
username password
-----------------
user1 user1
user2 user2
in group table
groupname admin
---------------
grp1 user1
grp2 user2
in usergroup table
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
The result will be the following list - {grp1,grp2,grp2,grp1}
Instead of {grp1,grp2}
If I change in Group Entity the fetch method to LAZY I'm getting the correct results
but hibernate throws me LazyException in another place...
Please assist what fetch method should I use and why ?
Thanks!
Lazy people will tell you to always use FetchType.EAGER counter-intuitively. These are the people who generally don't worry about database performance and only care about making their development lives easier. I'm going to say you should be using FetchType.LAZY for the increased performance benefit. Because database access is usually the performance bottleneck of most applications, every little bit helps.
If you do actually need to get a list of users for a group, as long as your call getUsers() from within a transactional session, you won't get that LazyLoadingException that is the bane of all new Hibernate users.
The following code will get you all groups without populating the list of users for those groups
//Service Class
#Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
The following code will get you all groups with the users for those groups at the DAO level:
//DAO class
#SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
Edit 1: Thanks to Adrian Shum for this code
For more info on the different types of FetchMode's see here
If you don't want to have to write a different DAO method just to access your collection object, as long as you are in the same Session that was used to fetch the parent object you can use the Hibernate.initialize() method to force the initialisation of your child collection object. I would seriously not recommend that you do this for a List<T> of parent objects. That would put quite a heavy load on the database.
//Service Class
#Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
I've not come across a situation where I've had to use the above code, but it should be relatively efficient. For more information about Hibernate.initialize() see here
I have done this in the service layer rather than fetching them in the DAO, because then you only have to create one new method in the service rather than making a separate DAO method as well. The important thing is that you have wrapped the getUsers() call within the transaction, so a session will have been created that Hibernate can use to run the additional queries. This could also be done in the DAO by writing join criteria to your collection, but I've never had to do that myself.
That said, if you find that you are calling the second method far more than you are calling the first, consider changing your fetch type to EAGER and letting the database do the work for you.
Although answer from JamesENL is almost correct, it is lacking of some very key aspect.
What he is doing is to force the lazy-loading proxy to load when the transaction is still active. Although it solved the LazyInitialization error, the lazy loadings are still going to be done one by one, which is going to give extremely poor performance. Essentially, it is simply achieving the same result of FetchType.EAGER manually (and with a even worse way, because we missed the possibilities of using JOIN and SUBSELECT strategy), which even contradict with the concern of performance.
To avoid confusion: Using LAZY fetch type is correct.
However, in order to avoid Lazy Loading Exception, in most case, you should have your repository (or DAO?) fetch the required properties.
The most inefficient way is to do it by accessing the corresponding property and trigger the lazy loading. There are some really big drawbacks:
Imagine what happen if you need to retrieve multiple level of data.
If the result set is going to be big, then you are issuing n+1 SQLs to DB.
The more proper way is to try to fetch all related data in one query (or a few).
Just give an example using Spring-data like syntax (should be intuitive enough to port to handcraft Hibernate Repository/DAO):
interface GroupRepository {
#Query("from Group")
List<Group> findAll();
#Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
Join fetching is equally simple in Criteria API (though seems only left join is available), quoted from Hibernate doc:
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();
Let's say I have two entities:
#Entity
public class Customer implements Serializable {
...
#OneToMany(cascade=ALL, mappedBy="customer")
public Set<Order> getOrders() {
return orders;
}
...
}
#Entity
public class Order implements Serializable {
...
#ManyToOne
#JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() {
return customer;
}
...
}
Then, I'm persisting Customer entity, and after that, Order entity with reference to previously added Customer.
When I retrieve this customer from database, and call getOrders, it returns empty set.
Is it normal behaviour? If it is, what can I do to automatically refresh this set when I add new Order entity?
Jpa does not maintain relationships for you, the application is required to set both sides of bidirectional relationships to keep them in synch with the database. Add order to the orders list when you set the order->customer relation and if customer is detached, merge it to have the changes to the collection picked up.
Otherwise you will need to explicitely refresh using em.refresh or a query with a refresh query hint after the transaction, or evict the customer from the caches. Either way, it requires a database hit that is easily avoided by just maintaining both sides of relationships.
If you retrieve the customer from the same transaction, then yes, it's expected behavior. The reason is that the EntityManager returns the order it has in its first-level cache, and that you created yourself, without adding any order to its set of orders. It's your responsibility to maintain the coherence of the object graph by maintaining the two sides of the association:
order.setCustomer(customer);
customer.addOrder(order);