How do I implement one-many relationship in ORMLite Android?
please find the example
public class A {
private String name;
#DatabaseField (foreign = true, foreignAutoRefresh = true, columnName = "A")
private B b;
#DatabaseField(columnName = "author")
private String authorName;
}
public class B {
#DatabaseField(generatedId = true, columnName = "id")
private long id;
#DatabaseField(columnName = "name")
private String name;
#ForeignCollectionField
Collection<A> aees;
}
B has collection of A. I am calling dao.create(b);
Now i create dao of b, since b has all the data. But the table B has only created with data, A is never created. Kindly could some one help?
Now i create dao of b, since b has all the data. But the table B has only created with data, A is never created. Kindly could some one help?
Right. You need to create the A items using:
for (A a : b.aees) {
aDao.create(a);
}
ORMLite does not automatically create those for you.
You might take a look a the source code of the foreign-collection example program.
You have to override the DAO of the B class, so when an object B is created or updated, the objects in the collection to be also updated.
Take a look at this question: Collections in ORMLite.
i was facing the same problem. My json was like:
{
"parent":{
"name":"ABC",
"children":[
{"childName":"1"},
{"childName":"2"},
{"childName":"3"}
]
}
}
i resolved the issue like this:
Parent parent = new Parent();
parent.setName("ABC");
while(get children one by one from json data)
{
childrenArray.add(new Child(its Name));
}
parentDAO.create(parent);
for(Child child : childrenArray)
{
child.setParent(parent);
childDAO.create(child);
}
Related
I want to build a simple HATEOAS API with Spring Boot and Spring Data REST.
Thus, I have created 2 different entities A and B. There is a Many-To-Many relation between both. Because the relation needs 2 more properties, I added a new relation R with these properties.
For the API, only A and B should be visible, R is only internal and has no repository. I'd like to represent A with a link to a collection of B.
It should be possible to create A, create B, link A to B or B to A without providing and returning the internal properties of R.
I guess it's a design problem?
Possible solutions:
I tried to create a custom method findByR_Bid() but I cant provide the own id to this method, so "/a/b" is not possible, it's always "/a/search/...".
Next, I've added a custom link with ResponseEntities.linkToCollection(B.class), but this will represent a link to "/b" without a reference to A's own id.
Next, I have implemented a method in A to return List from R. But this list is included internally as property instead of creating a Link.
A bit code:
#Entity
public class A {
#Id
private Long id;
#OneToMany(mappedBy = "a")
private List<R> r;
// getters
}
#Entity
public class B {
#Id
private Long id;
#OneToMany(mappedBy = "b")
private List<R> r;
// getters
}
#Entity
public class R {
#Id
private Long id;
#ManyToOne(optional = false)
private A a;
#ManyToOne(optional = false)
private B b;
// internal properties
#JsonIgnore
private String foo;
#JsonIgnore
private String bar;
// getters
}
For the JSON response I want to have something like this:
GET /a/1
"_links" : {
"self": {
"href": "http://localhost:8080/a/1"
},
"a": {
"href": "http://localhost:8080/a/1"
},
"bs": {
"href": "http://localhost:8080/a/1/b{?page,size,sort}",
"templated": true
}
}
It's tricky, but not impossible!
You can add B to A with a many-to-many relationship (and vica versa) using the same table:
#Entity
public class A {
#Id
private Long id;
#OneToMany(mappedBy = "a")
private List<R> r;
#ManyToMany
#JoinTable(name = "table_r", inverseJoinColumns = { #JoinColumn(name = "b_id") })
private list<B> b;
// getters
}
This way you force hibernate to use the same table for the man-to-many relationship which supports the R entity. Spring Data Rest will also map this many-to-many relationship to the endpoints, so /a/1/bs will work.
However accessing the extra properties of the relationship will be still tricky. (you need to use the /search endpoints.)
An other solution if you create a composite key for R (from A+B) instead of standard id. (Which is actually already exists, because the support table for a many-to-many relationship has a composite key based on a_id+b_id)
If you create a converter for the composite key then you can access the relationship properties like:
/R/23-34 (Relationship between a-23 and b-34)
It's not too nice but works.
I have an Entity including a collection of Embeddable objects as follows:
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "as")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ElementCollection
private Set<B> bs;
public B getB(String name) {
for(B b : bs)
if(b.getName().equals(name))
return b;
return null;
}
public void addB(B b) {
if(!bs.add(b))
throw new IllegalArgumentException("Duplicate ......");
}
....
}
#Embeddable
public class B {
#Column(nullable = false)
private String name;
#Temporal(TemporalType.TIMESTAMP)
#Column(nullable = false)
private Date creationTimestamp;
}
I'm using Spring Data to load and save my entity as follows:
Optional<A> a = aRepository.findById(aId);
B b = a.getB(...);
if(b == null) {
b = new B(...);
a.addB(b);
}
aRepository.save(a);
The code above is in a method annotatted with #Transactional.
When the method returns, I can see 3 duplicate embeddable objects in my database instead of one.
Any idea?
EDIT:
After a long debugging, I can confirm that Hibernate only inserts only one row for the single instance I add. However, when I return the created object from my REST controller, at some point the Jackson object mapper is involved to serialize my object before sending it back to the client, and here happens the two remaining INSERTs... I never saw that before... any help would be appreciated
More information:
The last 2 INSERTs are done when the SessionRepository commits the sessions changes - I precise that I use Spring Session. If that can help...
Issue happens here bs.add(b). Set adds an object would check if the object exists in the set. But in your case object b would always not the same as existing object because of creationTimestamp. creationTimestamp would be different from existing object. Because you new object, the time would be current datatime. So bs.add(b) would always be true.
Try to remove creationTimestamp and rerun your code to verify this.
You need to override equals() and hashCode() of B
Sorry if my post is duplicated or the tittle doesn't describe the topics, because I don't know how to describe this in the tittle, I look on internet, but I didn't find the solution.
I am using Java and JPA. The problem is the next :
I have a class A with an autogenerated key :
class A{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private List<B> listB;
}
And the class B with the id of this clas:
class B {
#EmbeddedId
private Bid id;
private String att;
}
class Bid {
private int idA;
private String text;
}
In a controller I want to create an object A, the problem is when I created the object A, I need to create the object B where the id of B contains the id of A which is autogenerated, and it is created in the moment when the entity is mapped to de database, I dont't know how to set the id autogenerated of A into the idB, maybe I should query to de database asking what is the las id of classA, but it seem bad.
Thanks in advance
Your case is a derived identifier case, where your entity B's identity was derived from the primary key of A. You can use #MapsId annotation for this case and your entities can be restructured like this:
#Entity
public class A {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#OneToMany(mappedBy="a")
private List<B> listB = new ArrayList<B>();
...
}
#Entity
public class B {
#EmbeddedId
private BId id;
#ManyToOne
#MapsId("idA")
private A a;
...
}
#Embeddable
public class BId {
private int idA;
private String att;
...
}
This is how you would persist the entities:
A a = new A();
BId bid = new BId();
bid.setAtt("text"); // notice that the idA attribute is never manually set, since it is derived from A
B b = new B();
b.setId(bid);
b.setA(a);
a.getListB().add(b);
em.persist(a);
em.persist(b);
See sample implementation here.
It would be useful to know which is the case scenario you are trying to solve in general because the structure you are using seems unnecessarily complex.
What is your real goal?
I have some trouble with Hibernate 4 and inheritance:
I use a ChildData class which inherit from BaseData by a JOIN inheritance strategy. My mapping is done by annotation in classes.
Everything is working fine except that when I delete a ChildData instance (with session.delete() or with a Hql query) the BaseData entry is also deleted.
I understand that in most case this is the awaited behavior, but for my particular case, I would like to preserve the BaseData entry no matter what for history purpose.
In other words I want all actions on the child class to be cascaded to base class except deletion.
I have already tried #OnCascade on the child class, with no success.
Is it a way to achieve this by code or do I have to use a SQL Trigger ON DELETE ?
EDIT :
Base Class
#Entity
#Table(name = "dbBenchHistory", uniqueConstraints = #UniqueConstraint(columnNames = "Name"))
#Inheritance(strategy = InheritanceType.JOINED )
public class DbBenchHistory implements java.io.Serializable {
private int id;
private String name;
private String computer;
private String eap;
private Date lastConnexion;
private Set<DbPlugin> dbPlugins = new HashSet<DbPlugin>(0);
private Set<DbSequenceResult> dbSequenceResults = new HashSet<DbSequenceResult>(
0);
public DbBenchHistory() {
}
public DbBenchHistory(int id, String name) {
this.id = id;
this.name = name;
}
public DbBenchHistory(int id, String name, String computer, String eap,
Date lastConnexion, Set<DbPlugin> dbPlugins,
Set<DbSequenceResult> dbSequenceResults) {
this.id = id;
this.name = name;
this.computer = computer;
this.eap = eap;
this.lastConnexion = lastConnexion;
this.dbPlugins = dbPlugins;
this.dbSequenceResults = dbSequenceResults;
}
#Id
#Column(name = "Id", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
//Getters/Setters
Child Class :
#Entity
#Table(name = "dbBench")
#OnDelete(action=OnDeleteAction.NO_ACTION)
public class DbBench extends DbBenchHistory {
private Set<DbProgram> dbPrograms = new HashSet<DbProgram>(0);
private Set<DbUser> dbUsers = new HashSet<DbUser>(0);
public DbBench() {
}
public DbBench(Set<DbProgram> dbPrograms,
Set<DbUser> dbUsers) {
this.dbPrograms = dbPrograms;
this.dbUsers = dbUsers;
}
//Getters/Setters
But I'm starting to think that I was wrong from the beginning and that inheritance was not the good way to handle this. If nothing shows up I will just go for BenchHistory - Bench being a simple one-to-one relationship
EDIT2 :
I edit while I can't answer my own question for insuficient reputation
I feel completly stupid now that I found the solution, that was so simple :
As I said, I was using hibernate managed methods : session.delete() or hql query. Hibernate was doing what he was supposed to do by deletintg the parent class, like it would have been in object inheritance.
So I just bypass hibernate by doing the deletion of the child class with one of the simplest SqlQuery on earth. And the base class entry remain untouched.
I understand that I somehow violate the object inheritance laws, but in my case it is really handy.
Thanks to everyone for your time, and believ me when I say I'm sorry.
I don't think Hibernate/JPA supports this. What you basically want is conversion from a subclass to a superclass, and not a cascading delete. When you have an object of the subclass, the members from the superclass are treated no different than the members of the subclass.
This can be solved through writing some logic for it though:
public void deleteKeepSuperclassObject(final ChildData childData) {
final BaseData baseDataToKeep = new BaseData();
//populate baseDataToKeep with data from the childData to remove
em.persist(baseDataToKeep);
em.remove(childData);
}
I have to test some code I have not myself written. It is an integration test: the application is running continuously on a server and my tests run against it.
The tests are Selenium tests, they launch a browser, execute some JavaScript inside it to simulate user actions and checks if the database is correctly updated. I have to restore the database to its initial state after each.
To do this, I use Spring annotations and Hibernate via DAO's I have not myself written.
The problem is that there are circular foreign keys. An object of class A has a OneToMany relationship with objects of type B, and there is also a ManyToOne association with the same class. I try to delete an object of type A and all its associated B's in the same transaction, but it doesn't work because Hibernate tries to set "defaultB" to null before deleting the object of type A. It is completely unnecessary to nullify it, although it makes sense to do it once the referred object of type B is deleted.
I (naively) thought that because the 2 operations were executed in the same transaction, deleting the object "a" of type A referring to (and referenced by) the object "b" of class B and deleting b at the same time would be no problem. However, I was plain wrong. It there is way to do this without changing the DB model (which I haven't written)?
Update 1: I don't understand why, when I execute mySession.delete(B), Hibernate tries to nullify a key it knows as non-nullable...any thoughts about this?
Update 2: there is a one-to-many relationship from class C to class B. Hibernate also tries to nullify the "c_id" field in the table corresponding to B, when I delete the C object that has this c_id. And that even though I delete the object of class B before its "parent". I know Hibernate reorders the queries and adds some stuff of its own, but I don't get the point of reordering queries that are already in the correct order to make them fail.
Here are (relevant parts of) the classes:
#Entity
#Table(name = "A")
public class A {
private Set<B> bs;
private B defaultB;
#OneToMany(mappedBy = "a", fetch = LAZY)
public Set<B> getBs() {
return bs;
}
public void setBs(Set<B> bs) {
this.bs = bs;
}
#ManyToOne(fetch = LAZY, optional = false)
#JoinColumn(name = "default_b_id", nullable = false)
public B getDefaultB(){
return defaultB;
}
public void setDefaultB(B defaultB) {
this.defaultB = defaultB;
}
}
#Entity
#Table(name = "B")
public class B {
private a;
#ManyToOne(fetch = LAZY, optional = false)
#JoinColumn(name = "A_id", nullable = false)
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
I try to delete an object of type A and all its associated B's in the same transaction
You should cascade the REMOVE operation for this if you don't want to have to remove all Bs manually. I would try the following (using cascade on both associations):
#Entity
#Table(name = "A")
public class A {
private Set<B> bs;
private B defaultB;
#OneToMany(mappedBy = "a", fetch = LAZY, cascade=CascadeType.REMOVE)
public Set<B> getBs() {
return bs;
}
public void setBs(Set<B> bs) {
this.bs = bs;
}
#ManyToOne(fetch = LAZY, optional = false, cascade=CascadeType.REMOVE)
#JoinColumn(name = "default_b_id", nullable = false)
public Strategy getDefaultB(){
return defaultB;
}
public void setDefaultB(B defaultB) {
this.defaultB = defaultB;
}
}
I cannot change these annotations. BTW, I do remove all associated B's manually, it's just that the queries Hibernate issues don't do what I want.
Ok... But then my guess is that you're not updating correctly both sides of the bidirectional association before remove the entities. This is typically done in defensive programming methods like this (in A):
public removeFromBs(B b) {
b.setA(null);
this.getBs().remove(b);
}
I assume that you want to delete a, but Hibernate does not allow it because b still refer to it?
Since you meta-model do not specify cascade delete, you need to "break" the link of b to a before deleting a. So do b.setA(null) before deleting a.