I'm running into a strange problem using the Toplink implementation of JPA. I'm writing a stand-alone application to manage loans.
I define a LOAN class that has a OneToMany (bi-directional) relationship with a NOTICE class. I created a GUI to display all the notices for a particular loan. In another part of my program I can send out late notices which is saved as a NOTICE. However, when I try to display all the notices for a loan, the late notice is NOT appearing. I've checked the database and an entry has been saved but for some reason the notice is not being pulled.
Only when I restart my application does the notice appear.
Because I've defined a OneToMany relationship, I, myself, am not making a "direct" query into the database - I'm letting JPA handle retrieving all the notices for me.
As a fix, I created a query to simply get all notices for a particular loan. This works. However, I thought by defining the OneToMany relationship between the two classes, this should be handled for me. It seems like something is not being refreshed properly... as if an "older" list of notices is being used instead of refreshing from the database?
How are you 'sending the late notice'? There needs to be a statement similar to:
MyLoanInstance.listOfNotices.add(MyNoticeInstance);
even if you already have a
MyNoticeInstance.setLoan(MyLoanInstance);
statement
Without that call, you will have to either completely reload the instance you are working with, or restart your application.
Eclipselink does not automatically update (until reloading everything) the collection on the M side of a 1:M relationship when you modify the 1 side, nor does it update the reference in the 1 side of the 1:M relationship if you modify the collection in the M side.
As a side note, you should consider checking out EclipseLink, it evolved out of what was TopLink, you should be able to directly swap the EclipseLink .jar with the TopLink .jar, if only to recieve a few depreciation warnings.
Eclipse Link
Have you tried to do a flush on your entity manager after you have persisted the NOTICE ?
[ entityManager.flush() ]
I suppose the following
#Entity
public class Loan {
private Integer id;
private List<Notice> noticeList;
#Id
#GeneratedValue
public Integer getId() {
return this.id;
}
#OneToMany(mappedBy="loan")
public List<Notice> getNoticeList() {
return noticeList;
}
}
#Entity
public class Notice {
private Integer id;
private Loan loan;
#Id
#GeneratedValue
public Integer getId() {
return this.id;
}
#ManyToOne(fetch=FetchType.LAZY)
public Loan getLoan() {
return this.loan;
}
}
You have said
In another part of my program I can send out late notices which is saved as a NOTICE
Maybe you have two EntityManager instances (one for each part of your program) so the last Notice saved is not showed because both entityManager does not share the same persistence context (One is not aware of the changes occured in the other). What you can do is clear your persistence context and then retrieve it again as follows
In anther part of your program (after persisting your Notice) call
entityManager.flush();
And when you want to retrive a Loan and its Notice
entityManager.clear();
Query query = entityManager.creatQuery("from Loan l left join fetch l.noticeList where l.id = :id");
query.setParameter("id", yourLoanId);
Check it out and tell me whether it works fine.
regards,
Related
I have been practicing one-to-one mapping in hibernate and don't understand this particular case. I have to say, the program is working fine and as I intended, but apparently I can omit a perist() call and it still works smoothly. The fact that it's working is good, but I want to know exactly why the call is optional. Let me write some details:
This is the user class, which is supposed to be the owning side of the mapping:
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String name;
#OneToOne
private Ticket ticket;
public User() {}
public User(String name) {
this.name=name;
}
}
And this is the ticket class that's supposed to be the dependent one:
#Data
#Entity
public class Ticket {
#Id
#GeneratedValue(generator="foreignGenerator")
#GenericGenerator(name="foreignGenerator", strategy="foreign",
parameters=#org.hibernate.annotations.Parameter(name="property", value="user"))
private Long id;
#OneToOne(optional = false, mappedBy="ticket")
#PrimaryKeyJoinColumn
private User user;
public Ticket() {
}
public Ticket(User user) {
this.user=user;
}
}
I am trying to test the "shared primary key" strategy in one-to-one mapping. As you can see I have set up the generator with foreign strategy, which is supposed to make Ticket's id the same as it's corresponding User's id.
#Bean
CommandLineRunner loadData() {
return args->{
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
User user=new User("Test User");
Ticket ticket=new Ticket(user);
//em.persist(user);
user.setTicket(ticket);
em.persist(ticket);
em.getTransaction().commit();
em.close();
//We don't have to call persist on user
};
}
}
This program runs perfectly. Uncommenting the line which calls persist on user makes no difference. I am assuming that persisting ticket, which has it's user property set, automatically saves the user as well. Therefore, the reason it makes no difference is that no matter if user is getting saved or not, it will get persisted when we call ticket.
I want to know if my assumption is correct and any additional links to articles/documentation would be greatly appreaciated. Especially I am wondering about this part that I said above-"I am assuming that persisting ticket, which has it's user property set, automatically saves the user as well." I couldn't find anything that would confirm or deny this. I know that the "shared primary key" approach in one-to-one mapping is the only use case of "foreign" generation strategy, so there are not a lot of posts about it, and whatever posts are there are getting overshadowed by "foreign key" during the search.
Any help regarding this or any other issue that might be wrong with the code provided above would be appreciated. Thanks for taking your time to read this
The JPA specification states this behavior is wrong:
Looking at the 3.0 release:
section "3.2.2. Persisting an Entity Instance" implies user is unmanaged after your persist (you can check with the em.contains method).
Section "3.2.4. Synchronization to the Database" covers the flush/commit which states:
• If X is a managed entity, it is synchronized to the database.
..
◦ For any entity Y referenced by a relationship from X, where the relationship to Y has not been annotated with the cascade element value cascade=PERSIST or cascade=ALL:
▪ If Y is new or removed, an IllegalStateException will be thrown by the flush operation (and the transaction marked for rollback) or the transaction commit will fail.
User is new, so this should be resulting in an exception. That it works might be a glitch in how Hibernate is handling the #PrimaryKeyJoinColumn annotation (speculation on my part) and custom "foreignGenerator".
This is not a pattern I'd suggest you rely on, and should instead call persist to avoid inconsistencies with the behavior on other mapping setups.
I'm updating an existing code that handles the copy or raw data from one table into multiple objects within the same database.
Previously, every kind of object had a generated PK using a sequence for each table.
Something like that :
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
In order to reuse existing IDs from the import table, we removed GeneratedValue for some entities, like that :
#Id
#Column(name = "id")
private Integer id;
For this entity, I did not change my JpaRepository, looking like this :
public interface EntityRepository extends JpaRepository<Entity, Integer> {
<S extends Entity> S save(S entity);
}
Now I'm struggling to understand the following behaviour, within a spring transaction (#Transactional) with the default propagation and isolation level :
With the #GeneratedValue on the entity, when I call entityRepository.save(entity) I can see with Hibernate show sql activated that an insert request is fired (however seems to be only in the cache since the database does not change)
Without the #GeneratedValue on the entity, only a select request is fired (no insert attempt)
This is a big issue when my Entity (without generated value) is mapped to MyOtherEntity (with generated value) in a one or many relationship.
I thus have the following error :
ERROR: insert or update on table "t_other_entity" violates foreign key constraint "other_entity_entity"
Détail : Key (entity_id)=(110) is not present in table "t_entity"
Seems legit since the insert has not been sent for Entity, but why ? Again, if I change the ID of the Entity and use #GeneratedValue I don't get any error.
I'm using Spring Boot 1.5.12, Java 8 and PostgreSQL 9
You're basically switching from automatically assigned identifiers to manually defined ones which has a couple of consequences both on the JPA and Spring Data level.
Database operation timing
On the plain JPA level, the persistence provider doesn't necessarily need to immediately execute a single insert as it doesn't have to obtain an identifier value. That's why it usually delays the execution of the statement until it needs to flush, which is on either an explicit call to EntityManager.flush(), a query execution as that requires the data in the database to be up to date to deliver correct results or transaction commit.
Spring Data JPA repositories automatically use default transactions on the call to save(…). However, if you're calling repositories within a method annotated with #Transactional in turn, the databse interaction might not occur until that method is left.
EntityManager.persist(…) VS. ….merge(…)
JPA requires the EntityManager client code to differentiate between persisting a completely new entity or applying changes to an existing one. Spring Data repositories w ant to free the client code from having to deal with this distinction as business code shouldn't be overloaded with that implementation detail. That means, Spring Data will somehow have to differentiate new entities from existing ones itself. The various strategies are described in the reference documentation.
In case of manually identifiers the default of inspecting the identifier property for null values will not work as the property will never be null by definition. A standard pattern is to tweak the entities to implement Persistable and keep a transient is-new-flag around and use entity callback annotations to flip the flag.
#MappedSuperclass
public abstract class AbstractEntity<ID extends SalespointIdentifier> implements Persistable<ID> {
private #Transient boolean isNew = true;
#Override
public boolean isNew() {
return isNew;
}
#PrePersist
#PostLoad
void markNotNew() {
this.isNew = false;
}
// More code…
}
isNew is declared transient so that it doesn't get persisted. The type implements Persistable so that the Spring Data JPA implementation of the repository's save(…) method will use that. The code above results in entities created from user code using new having the flag set to true, but any kind of database interaction (saving or loading) turning the entity into a existing one, so that save(…) will trigger EntityManager.persist(…) initially but ….merge(…) for all subsequent operations.
I took the chance to create DATAJPA-1600 and added a summary of this description to the reference docs.
I am a bit confused about managing relationship in JPA.
basically I have two entities with a One to Many relationship
A configuration can have have a one or many email list associated with it.
#Entity
public class Config {
#OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<Email> emailReceivers;
}
#Entity
public class Email {
#ManyToOne
private Config owner;
}
In an EJB and during update/merge operation wherein I would edit the list of emails associated with a configuration,
I thought that I dont need to explicitly call the delete operation on my email Entity and I would just manage the relationship by deleting the email in my configuration email list.
#Stateless
public class ConfigFacadeImpl implements ConfigFacade{
#EJB
private ConfigDao configDao;
#EJB
private EmailDao emailDao;
#Override
public void update(Config Config, List<Email> emailsForDelete) {
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
config.getEmailReceivers().remove(email);
}
}
configDao.update(config);
}
}
If I don't execute the delete and only remove it from the list, it wont erase my table row.
The UI and the database is now not in sync as the UI would not show the email(s) that I have deleted but when you check the database, the row(s) are still there.
Is it required? I thought JPA would handle this for me if I would just remove it in my entities.
UPDATE
I have tweaked my code to get the entity from the database first before making any changes but still it is not deleting my child email entities. I wonder if this is an apache derby issues. (This is the correct way right as I am passing my entities from my JSF managed bean into my EJB so I need to get the sync from the DB first.)
#Override
public void update(Config config, List<Email> emailsForDelete) {
Config configTemp = configDao.find(config.getId());
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
configTemp.getEmailReceivers().remove(emailTemp);
}
}
configDao.update(config);
}
Since you have already defined cascade type = CascadeType.ALL, JPA should take care of the deletion. Explicit Delete statement is not required.
These two statements are not required:
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
Instead, you may want to just find the matching emailReceiver in config.getEmailReceivers() and remove the matching EmailReceivers as you are doing. There is no need to load the Email entity from the database.
EDIT: To delete orphan objects, you may want to include CascadeType.DELETE_ORPHAN cascade attribute along with CascadeType.ALL.
This is the same issue as in Why merging is not cascaded on a one to many relationship
Basically, JPA can only cascade over entities in your collection. So changes to child objects removed from the collection are never putinto the context, and so can't be pushed to the database. In this case, the oneToMany is controlled by the manytones back pointer, so even collection changes won't show up unless the child is also merged. Once a child is pruned from the tree, it needs to be managed and merged individually for changes to it to be picked up.
With JPA 2.0, you can use the option orphanRemoval=true in parent entity
Example:
#Entity
public class Parent {
...
#OneToMany(mappedBy="parentId",cascade=CascadeType.ALL, orphanRemoval=true)
private List<Child> childList;
...
}
My bean looks like that:
#Entity
public class Fattura {
#Id
Long id;
#NotEmpty
String numero;
#Min(value=0)
Double importo;
Key<User> utente;
// gets & sets....
}
The "utente" property is the key of another bean I created: a "Fattura" can have only one "User", one "User" can have many "Fattura"s
My Spring MVC controller will manage a request for a list of Fattura and display them in a simple jsp:
#RequestMapping( value = "/fatture" , method = RequestMethod.GET )
public ModelAndView leFatture() {
ModelAndView mav = new ModelAndView("fatture");
mav.addObject("fatture",fatturaService.listFatture());
return mav;
}
the code of the jsp is really simple: only a foreach cycle in a table
My question is:
how can I display the "utente"?
The only thing I have is its key, but I'd like to do something like ${fattura.utente.firstName} in my JSP, how can I do it?
Unfortunately you would have to manually fetch "utente" in your DAO class. There is no automatic fetching in Objectify like in Twig. In my POJOs I have following fields
#Transient private Organization sender; // Pickup location (for client RPC)
transient private Key<Organization> senderKey; // Pickup location (for Datastore)
I load entity from Datastore and then load manually Organization using senderKey.
In new Objectify4 you'll be able to do what you want like this:
class Beastie {
#Parent
#Load
ParentThing parent;
#Id Long id;
#Load({"bigGroup", "smallGroup"})
SomeThing some;
#Load("bigGroup")
List<OtherThing> others;
#Load
Ref<OtherThing> refToOtherThing;
Ref<OtherThing> anotherRef; // this one is never fetched automatically
}
Here is evolving design document of new version.
Update at Nov 17, 2011: This is big news. Twig author, John Patterson, joined Objectify project today.
I know it sounds annoying that you have to manually fetch the two objects, but it's actually very useful to know that you're doubling your work and time to do this - each "get" call take a while and the second won't start until the first is complete. It a typical NoSQL environment, you shouldn't often need to have two separate entities - is there a reason that you do?
There are only two reasons I can easily think of:
The class references another object of the same type - this is the example in the Objectify documentation, where a person has a reference to their spouse, who is also a person.
The class that you're embedding the other into ("Fattura" in your case) has masses of data in it that you don't want fetched at the same time as you want to fetch the "User" - and you need the user on it's own more often than you need the "Fattura" and the "User". It would need to be quite a lot of data to be worth the extra datastore call when you DO want the "Fattura".
You don't necessarily have to use temporary field for just getting a object.
This works:
public User getUtente() {
Objectify ofy = ObjectifyService.begin();
return ofy.get(utenteKey);
}
This will of course do a datastore get() each time the getter is called. You can improve this by using #Cached on your User entity, so they turn into memcache calls after the first call. Memcache is good, but we can do a little better using the session cache:
public User getUtente() {
Objectify ofy = myOfyProvider.get();
return ofy.get(utenteKey);
}
The key thing here is that you need to provide (through myOfyProvider) an instance of Objectify that is bound to the current request/thread, and that has the session cache enabled. (ie, for any given request, myOfyProvider.get() should return the same instance of Objectify)
In this setup, the exact same instance of User will be returned from the session cache each time the getter is called, and no requests to the datastore/memcache will be made after from the initial load of this Entity.
I'm using JPA on a SWING application in JAVA that connects to an Apache DERBY embedded database. I use Netbeans as my IDE and use many of the "supposedly" helpful templates. My problem is simple, but it's difficult for me to explain so I will paste the relevant code here and try to explain at the bottom.
#Entity
public class AnioLectivo implements Serializable, Comparable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#OneToMany(mappedBy = "anioLectivo", cascade=CascadeType.ALL)
private List<Compensatorio> compensatorios;
...
}
#Entity
public class Compensatorio implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#ManyToOne
private AnioLectivo anioLectivo;
...
}
These two are the entities that i want to persist.
public class AnioLectivoJpaController
{
public void edit(AnioLectivo anioLectivo) throws NonexistentEntityException,
Exception
{
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
AnioLectivo persistentAnioLectivo = em.find(AnioLectivo.class,
anioLectivo.getId());
...
List<Compensatorio> compensatoriosOld =
persistentAnioLectivo.getCompensatorios();
List<Compensatorio> compensatoriosNew = anioLectivo.getCompensatorios();
...
List<Compensatorio> attachedCompensatoriosNew = new ArrayList<Compensatorio>();
for (Compensatorio compensatoriosNewCompensatorioToAttach : compensatoriosNew) {
compensatoriosNewCompensatorioToAttach =
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
attachedCompensatoriosNew.add(compensatoriosNewCompensatorioToAttach);
}
compensatoriosNew = attachedCompensatoriosNew;
anioLectivo.setCompensatorios(compensatoriosNew);
...
}
This is a class that netbeans generates using the annotations of the entity AnioLectivo that i pasted before. As you can see, i only pasted the code relevant to the problem to keep it simple because i know thanks to the debug tool of netbeans that the problem is here.
Now I'll try to explain exactly what happens.
I create instances of AnioLectivo in one part of the program and persist them ok. Then in another part i must create and add instances of Compensatorio to the Compensatorio's List in an instance of AnioLectivo. Now I want to save this modification, which I assume is made using the edit method in the class AnioLectivoJpaController and I found this error:
java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.
at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.findInternal(EntityManagerImpl.java:309)
at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.getReference(EntityManagerImpl.java:176)
at org.sigeb.local.service.dao.jpa.AnioLectivoJpaController.edit(AnioLectivoJpaController.java:113)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.guardarCambios(AdministrarCursosPopUp.java:574)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.jBGuardarCambiosActionPerformed(AdministrarCursosPopUp.java:394)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.access$1000(AdministrarCursosPopUp.java:44)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp$11.actionPerformed(AdministrarCursosPopUp.java:204)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
...
the problem, as I see it, occurs in this line of code in the edit method of AnioLectivoJpaController:
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
Why? Well if you see the entities, I have defined that the id of all entities are to be generated by the persistence unit, but this only happens when the entity itself is told to persist. As I create the Compensatorio's instances I never set the id explicitly and when it arrives to that line I quoted up there, compensatoriosNewCompensatorioToAttach.getId() returns null.
It's my understanding that ORM's like JPA have Persistence by Reachability, that allows that if an object A is related to an object B, persisting A also persists B. But in this case it seems like it's implemented in a very inconvenient way(at least for me), because it forces me to persist every object of my collection explicitly when it would be more usefull to persist the object that owns that collection and then the objects in that collection be persisted automatically
Is there something I'm doing wrong?, maybe I should face this problem from another angle, but I don't know how, or if any, what angle?. Why does the people of netbeans make that template that way, why is it useful to execute that method to try to search the objects in the DB and bring it to the persistence context, do i need to persist every object myself? if that's so why do they claim to have Persistence by Reachability if the persistence can only be made in one direction only.
I'm clearly wrong in this, what I'm seeking it's a coherent explanation of how would have to be explicited the relationship between those entities(if i actually did a mistake in the way i created them, because in every book and tutorial i read it's done like that) to make it work so i don't need to persist every object of that collection, or, if i need to drop that template from netbeans and make the code for all the CRUD operations myself, i will like to hear advice on how is convenient to proceed in this case.
It seems to be you can call em.merge(anioLectivo) insted of all this code.