I have a class A :
#Entity
public class A {
...
#ManyToMany
private Set<Ref> refs = new HashSet<Ref>();
...
}
And a class Ref :
#Entity
public class Ref {
// no link to A
}
I want to delete A and A_Ref but not Ref, but i get a org.hibernate.exception.ConstraintViolationException
Is there a simple way to do it or should i explicitely create a A_Ref class ?
Thanks
Edit :
I was attempting to delete my list of A directly in hql. I did it in object (broke the relations) and it worked (cascade + orphan deletion).
I am not sure, if I got you right, but from what I understood my first guess is, that you havent used all necessary annotations to make sure, that hibernate will automatically remove necessary references in Ref by itself.
Within a manyToMany relation, you can use joinTables and cascadetypes to make sure, that hibernate knows where to delete all necessary relations by itself without creating an own domainObject for it.
There are plenty of nice guides how to manage it. On first view the guide from mkyong looks pretty good. Note: He annotated getters instead of variable declarations! (which is just a question of taste).
Related
I have 2 Entitites, one maps to a database table, the other to a database view.
The data of the view depends on the table.
#javax.persistence.Table(name = "BOOKING_INFO", schema = "BOOKING")
#Entity
public class BookingInfo extends AbstractBooking {
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
public class BookingView extends AbstractBooking {
This works fine in most cases, however when we write (insert or update) the Booking entity and then do queries (in my case a count) on the BookingView entity, we get stale data.
Why this happens is clear to me (hibernate caching, it only flushes when it detects that a select needs some data flushed).
So if I would do a query on the Booking entity, it would trigger a flush.
I have found the #Synchronize Annotation in Hibernate which sounds like it should fix this problem, like this:
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
#Synchronize("BOOKING.BOOKING_INFO")
public class BookingView extends AbstractBooking {
However this does not make any difference (flush only happens at the end of the transaction). Also the documentation I have found about this annotation is quite lacking and not very helpful.
EDIT: I also tried #Synchronize("BOOKING_INFO") (without the schema name, and also lowercase, but that made no difference)
The docs say that it is mostly used with #Subselect but it is not a must (I don't want that).
Has anyone ever successfully used this annotation?
Is there any other way to handle database views in Hibernate?
Am I missing something else?
Thanks to a colleague we were able to debug and fix this, the problem was that our Hibernate naming-strategy lowercased our table-names, so the correct annotaiton is:
#Synchronize("BOOKING.booking_info")
How to debug this:
set breakpoints in Hibernates ActionQueue class in the areTablesToBeUpdated methods.
There we saw that it compared "BOOKING.BOOKING_VIEW" to "BOOKING.booking_view".
We think this is a bug in hibernate because it should either apply the naming-strategies also to the values from #Synchronize or compare these case-insensitive (which could theoretically lead to too many flushes if you have a crazy database which uses tables with the same name only differentiated by casing).
Created a Hibernate issue: https://hibernate.atlassian.net/browse/HHH-10002
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
Say I have the following Java class, which is owned by a vendor so I can't change it:
public class Entry {
private String user;
private String city;
// ...
// About 10 other fields
// ...
// Getters, setters, etc.
}
I would like to persist it to a table, using JPA 2.0 (OpenJPA implementation). I cannot annotate this class (as it is not mine), so I'm using orm.xml to do that.
I'm creating a table containing a column per field, plus another column called ID. Then, I'm creating a sequence for it.
My question is: is it at all possible to tell JPA that the ID that I would like to use for this entity doesn't even exist as a member attribute in the Entry class? How do I go about creating a JPA entity that will allow me to persist instances of this class?
EDIT
I am aware of the strategy of extending the class and adding an ID property it. However, I'm looking for a solution that doesn't involve extending this class, because I need this solution to also be applicable for the case when it's not only one class that I have to persist, but a collection of interlinked classes - none of which has any ID property. In such a scenario, extending doesn't work out.
Eventually, I ended up doing the following:
public class EntryWrapper {
#Id
private long id;
#Embedded
private Entry entry;
}
So, I am indeed wrapping the entity but differently from the way that had been suggested. As the Entry class is vendor-provided, I did all its ORM work in an orm.xml file. When persisting, I persist EntryWrapper.
I don't have much experience with JPA, but I wouldn't extend your base classes, instead I would wrap them:
public class PersistMe<T> {
#Id
private long id;
private T objToWrap;
public(T objToWrap) {
this.objToWrap = objToWrap;
}
}
I can't test it, if it doesn't work let me know so I can delete the answer.
I have a problem with JPA 1.0 (OpenJPA)
Following situation
#Entity
public class A{
private Long aId;
private List<B> bEntities;
//myId getter and setter
#OneToMany(mappedBy="referencedA")
public List<B> getBEntities(){
return bEntities;
}
public void setBEntities(List<B> bEntities){
this.bEntities = bEntities;
}
}
,
#Entitiy
public class B{
private Long bId;
private Long aId;
private A referencedA;
//aId/bId getter and setter
#ManyToOne
#JoinColumn(name="A_ID", referencedColumnName="A_ID")
public A getReferencedA(){
return referencedA;
}
public void setReferencedA(A referencedA){
this.referencedA = referencedA;
}
}
If I perform the following JPQL-Query it works as expected, meaning that each attribute is filled:
select object(o) from B o
But if I want to get all As:
select object(o) from A o
and I try to get the B-List via aReceivedAObject.getBEntities(); it returns null.
If I change or rather extend the OneToMany annotation as follows:
#OneToMany(mappedBy="referencedA", fetch=FetchType.EAGER)
everything works as expected.
BUT I need lazy fetching because everything else will be much too slow.
I really hope that someone can help me solving the issue as I'm stuck to the problem for three days now :(
Notes, in case its important:
I (must) use Websphere 6.1 (with Feature-Pack for EJB 3.0), which uses OpenJPA 1.0. So it's a JavaEE project.
As far as I understood the OpenJPA-Doc I need to enable the enhancement, what I have done as described here http://www.ibm.com/developerworks/websphere/techjournal/0612_barcia/0612_barcia.html#sec4f
But this doesn't seem to make any difference :(
Thanks in advance!!!
Kind Regards,
asotbb
//edit: corrected typo: changed "public List getBEntities()" to "public List getBEntities()"
And from another class I call ADAL's
findAll method.
Yeah that is your issue, if it is not persistence aware, you will need to make that class aware of the persistence context if you want ReceivedAObject.getBEntities() to work with lazy loading.
Having persistence conexts all over the place though can get messy as you may be forced to manually merge entities between them. Another way you could do it is have that getBEntities() method be wrapped in the Data Access Layer (ADAL).
If you use OneToMany in lazy mode, and you want to use it outside of the transaction, it will be null, or the empty list, depends on what is the initialized filed value (inside of the transaction the JPA will make another query to select, so it will be a filled list). Use select o from A o join fetch o.as a (if in the class B the List as field called as). Or use left join fetch, depends on your needs.