I am trying to learn how to work with hibernate, and until now i thought i was doing ok...
The problem is, i have a one to many relationship that i can't update/delete.
My DB is pretty basic, i have a ClientsBasic that has a one to many relationship with IndirectClients (which simply has a ClientsBasic ID and a URL, both keys because you can have for the same ID lots of URLs)
ClientBasic:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "clientsBasic", cascade=CascadeType.ALL)
public List<IndirectClients> getIndirectClients() {
return this.indirectClients;
}
public void setIndirectClients(List<IndirectClients> indirectClients) {
// this.indirectClients = indirectClients;
this.indirectClients.clear();
this.indirectClients.addAll(indirectClients);
}
ClientDao:
public ClientsBasic save(ClientsBasic client) throws HibernateException {
Transaction tx = null;
tx = session.beginTransaction();
session.saveOrUpdate(client);
tx.commit();
log.info("Client saved with id: " + client.getClientId());
return client;
}
Now if i try to delete ClientsBasic, it will delete both ClientsBasic and all related indirectClients, so its working as expected, but if i simply try to update/delete and entry in indirectClients it doesn't work.
Example:
I create a new Client
ClientsBasic cb = new ClientsBasic("company_1", 1234, "company_1#email.com");
cbDao.save(cb);
And then a new Indirect Client
List<IndirectClients> indirectClientsSet= new ArrayList<IndirectClients>();
indirectClientsSet.add(new IndirectClients(new IndirectClientsId(cb.getClientId(), "www.url.test_1.com"), cb));
cb.setIndirectClients(indirectClientsSet);
cbDao.save(cb);
Now if i try to change the url like this
ClientsBasic cb = cbDao.findClientById(1);
List<IndirectClients> indC = cb.getIndirectClients();
indC.get(0).getId().setUrl("TEST");
cb.setIndirectClients(indC);
cbDao.save(cb);
no changes are made in the DB.
Can someone please help me?
Thank you.
If your IndirectClients is defined as an Entity it has its own life cycle, meaning you have to persist/delete instances separately from their ClientBasic parent.
If you want a scenario where all children are managed through their parent relation, consider using ElementCollection.
See also JPA: When to choose Multivalued Association vs. Element Collection Mapping
Related
I have a probléme when i want to persist 2 objet in database
In Request class:
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "request")
private List documents;
In Document class:
#JoinColumn( referencedColumnName = "ID_REQUEST")
#ManyToOne
Request request
the problem is that when I add request the I find that the 2 object are persisted but in the table Document lD_REQUEST IS ALWAYS NULL
THANK YOU IN ADVANCE and sorry for my English
You need to declare what is the column on the document table that contains the request Id:
#JoinColumn(name = "PARENT_REQUEST")
where PARENT_REQUEST is the name of the column on your Document table
While persisting new data into the database, you've probably added documents to Request's list but forgot to set Request's object to all of your documents on the other side.
Check that you did both things (the following is the example):
Request request = new Request();
//initialization of request
for (...) { //iterate over all document candidates
Document document = new Document();
//initialization of document
document.setRequest(request); //check this!
request.getDocuments().add(document);
}
Also, it seems that you don't have a not-null constraint on ID_REQUEST column. Add this so you won't have this broken data with nullable ID_REQUEST field in the future (constraint violation exception will be raised instead in this kind of situations).
I tried to update a row in my DB using Ebean in my Play! Framework program.
Here is the class of the entity I would like to update.
Transaction.java
#Entity
#Table(name = "transactions")
public class Transaction extends Model{
#Id
#GeneratedValue
public int id;
#OneToOne
#JoinColumn(name = "car_fk")
public Car car;
#OneToOne
#JoinColumn(name = "user_lender_fk")
public User user;
#Version
public Timestamp from_date;
#Version
public Timestamp to_date;
public boolean availability; // true -> Available.
public String status;
}
And here is the metho I use to update it:
Transaction transaction = new Transaction();
transaction.car = concernedCars.get(i);
transaction.user = currentUser;
transaction.from_date = Tools.StringAndroidToTimestamp(dateFrom);
transaction.to_date = Tools.StringAndroidToTimestamp(dateTo);
transaction.status = Constants.WAITING_FOR_ANSWER;
try{
Ebean.update(transaction);
}catch(OptimisticLockException e){
Logger.info(e.toString());
}
And if necessary, my method to convert a String to Timestamp:
public static Timestamp StringAndroidToTimestamp(String s){
String toConvert = s.substring(0, s.length()-2);
Logger.info("ToConvert = "+toConvert);
Timestamp timestamp = null;
try{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsedDate = dateFormat.parse(toConvert);
timestamp = new Timestamp(parsedDate.getTime());
}catch(Exception e){
Logger.info("Exception date = " +e.toString());
}
return timestamp;
}
Of course, I get the fabulous error:
javax.persistence.OptimisticLockException: Data has changed. updated
[0] rows sql
What did I do wrong?
There are few ways you can handle this.
1) Use #EntityConcurrencyMode(ConcurrencyMode.NONE) before class name
2) Use raw update query.(preferred)
I was facing too much problem because of the same ebean.update throwing optimistic lock exception, finally I did raw update query and it worked for me.
I can see some problems from here:
You are using two Version fields. I don't know if that is alright to Ebean but usually one is enough. Also I think Ebean will manage those fields by itself, so it may be better for you to specify a version field that you don't want to use (an int to serve as a counter or a timestamp of last change);
You are calling update but it seems you are really creating a new transaction, so you should be using Ebean.save
Regarding the error, that exception is thrown when you try to update a record that has changed between the time you loaded it and the update. To find out that the record has changed, Ebean uses the values from the Version columns.
So the update in your code would generate a SQL similar to this:
UPDATE transactions
SET car_fk=<SOME_VAL>, ...
WHERE id=null AND from_date=<OTHER_VAL> AND to_date=<ANOTHER_VAL>
which wouldn't update any record and throw that exception.
I'm posting this information because I don't know a easy way to disable optimistic locking in the Ebean version that comes with Play <2.4.x and you probably will find that error again. In general you can minimize it using a version field and using transactions or reload/retry the operation.
I have an alternative solution to this problem
you can use:
#EntityConcurrencyMode(ConcurrencyMode.NONE)
in your Entity Class
This will disable the optimistic locking concurrent modification check
the new sql query will be:
update TABLE-NAME SET PARAM1=? WHERE ID = ?
the EntityConcurrencyMode comes in package
package com.avaje.ebean.annotation;
I have the following ManyToMany association within my Project:
Class User:
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(
joinColumns={#JoinColumn(name="user_id")},
inverseJoinColumns={#JoinColumn(name="antrag_id")}
)
private List<Antrag> antraege;
And Class Antrag:
#ManyToMany(mappedBy="antraege",cascade=CascadeType.ALL)
private List<User> users;
I am trying to correctly insert a new record, this - I think works not correctly. When I'm trying to "reselect" all Antraege of User, I am getting a NullPointerException on antraege.getUsers(). Only when I restart the java swing application, antraege.getUsers() doesn't return null.
Code for Inserting my new ManyToMany record:
Antrag antrag = new Antrag();
antrag.setBetreff(betreff);
antragBusiness.create(antrag);
betreuer.getAntraege().add(antrag);
currentUser.getAntraege().add(antrag);
userBusiness.update(betreuer);
userBusiness.update(currentUser);
Where my create method uses persist and the update method uses merge.
As I've said, they get inserted correctly into the database, but when I try to call:
List<Antrag> antraege = (List<Antrag>) user.getAntraege();
for(Antrag a : antraege){
News n = new News();
n.setDatum(a.getCreatedAt());
n.setStatus(a.isStatus());
n.setThema(a.getInfo());
List<User> beteiligte = a.getUsers();
System.out.println("Amount users " + beteiligte.size());
for (User z : beteiligte) {
System.out.println(z.getNachname() + " " + z.getId());
if(z.getId() != user.getId()){
n.setZustaendiger(z);
}
}
newsList.add(n);
}
-> Amount users returns 0 :(
So - in consequences: Antraege are added to Users, but users not to Antraege?
Edit: As it seems, my insertion of a new ManyToMany record is not correct.
Can anyone explain me the correct way ?
First of all sorry for my dump english but i would ask a little help..
I have two oracle Db tables (events,documents) and i made two entity classes.
one:
public class BDocuments implements Serializable {
#JoinColumn(name = "B_EVENT_ID", referencedColumnName = "ID")
#ManyToOne
private BEvents bEventId;
...
...
two:
public class BEvents implements Serializable {
#Id
#Basic(optional = false)
#GeneratedValue(generator="EventSeq")
#SequenceGenerator(name="EventSeq",sequenceName="B_EVENTS_SEQ", allocationSize=1)
#Column(name = "ID")
private Integer id;
...
...
the id field of event table is the foreign key for the documents table event_id field..
Whene the user create a new event on the view the program create a new instance to the events table and if the events has an attached document then i’ll create a instance to the documents table. And of course the new event instance’s id will be the new document instance’s event_id..
it workd fine.. there is no probleme if i check the tables with TOAD i can see the 2 new instance..
After this i’ll see the new event ont he view and if I’ll click on this int he new dialog window i should see the events details.. like does this event has any attached documents..
JSF:
<p:column>
<f:facet name="header">
<h:outputText value="Esemény ID"/>
</f:facet>
<h:commandLink value="#{item.id}" >
<p:ajax listener="#{mainWorkingBean.showChosedEventDetails('dlgshowevent',item.id)}" />
</h:commandLink>
</p:column>
<p:column>
There is a managed beans that has a metod showChosedEventDetails() and after some validation check it invoke the DAO EJB’s following method, and then create a data table for the view by the returned List.
This is EJB method:
public List<BDocuments> showAllDocumentsOnEvents(Integer chosedEventid) {
if (chosedEventid != null) {
try {
System.out.println("showAllDocumentsOnEvents: event id:" + chosedEventid);
assert emf != null;
EntityManager em = null;
em = emf.createEntityManager();
List<BDocuments> documentList = new ArrayList();
BEvents documentDetailsInstance = (BEvents) em.createNamedQuery("BEvents.findById").setParameter("id", chosedEventid).getSingleResult();
//get the event instance by the id that received in the method paramter…
System.out.println("showAllDocumentsOnEvents NEW CHOSED INSTANCE: " + documentDetailsInstance);
Collection<BDocuments> allDocumentsCollection = documentDetailsInstance.getBDocumentsCollection(); //a #ManytoOne..
System.out.println("showAllDocumentsOnEvents EVENTINSTANCE: " + documentDetailsInstance.getEventText());
System.out.println("showAllDocumentsOnEvents COLLECTION: " + allDocumentsCollection.toString());
System.out.println("showAllDocumentsOnEvents NEW COLLECTION SIZE..: " + allDocumentsCollection.size() + "collection: " + allDocumentsCollection.toString()); // lame debugging.. checking the values..
int x;
for (x = 0; x < allDocumentsCollection.size();) {
System.out.println("Show all: for ciklus: Iterables kiszedve: " + Iterables.get(allDocumentsCollection, x));
BDocuments document = Iterables.get(allDocumentsCollection, x);
documentList.add(document);
x++;
} //check the collection and put the elements to the list.. .
return documentList;
} catch (NullPointerException e) {
System.out.println(e.getMessage());
return null;
}
} else {
System.out.println(" showAllDocumentsOnEvents ELSE ÁG...showAllDocumentsOnEvents: event id:" + chosedEventid);
return null;
}
}
It is ALSO work fine BUT the Collection allDocumentsCollection is empty!
I couldn’t be empty because i can see in the database there is in everything what should be in.
And now comming the real big question.. after i make clean and build and redeploy in netbeans and then click ont he same event on the view the Collection allDocumentsCollection WILL NOT BE empty…
I’m really don’t know why is this Collection empty before the redeploy.. and why not after the redeploy..
I would be really thankfull if somebody could help me..
thank you!
Odd that you showed everything but the getBDocumentsCollection mapping that is causing you problems, or the code used to set the relationships. But since you are saying the database is populated correctly and this is a bidirectional relationship, it is a fairly common mistake to only populate one side of the relationship. When you set the BDocuments.bEventId with a BEvents instance, you must also add that BDocuments instance to the BEvents.BDocumentsCollection to keep the relationship in synch with the database. JPA does not maintain relationships for you, so you either set both sides, or constantly refresh the object from the database when the transaction commits using em.refresh or provider specific api. Since refreshing has additional overhead, it is much better to just always keep both sides in sync.
I have created a simple test in the java api (see below). I then set the neo4j-server.properties to that db and restart neo4j. I'm expecting to see two nodes and a relationship using the web browser localhost:7474/browser.
When code below is executed, the for loop does detect a relationship, however it does not display (nor are they returned by CYPHER query) in the browser.
I'm using 2.0.1 in the java and neo4j server. Is my expectation in error?
Transaction tx = gdb.beginTx();
try
{
Label courseLabel = DynamicLabel.label( "Book" );
Label courseLabelP = DynamicLabel.label( "Person" );
Node a = gdb.createNode(courseLabel), b = gdb.createNode(courseLabelP);
Relationship rel = a.createRelationshipTo( b, CourseRelTypes.HAS_AUTHOR );
for(Relationship r : b.getRelationships(CourseRelTypes.HAS_AUTHOR)) {
System.out.println("has rel");
}
}
finally {
tx.close();
}
You need to do a tx.success() to commit that transaction