Jpa: How to Have One-To-Many Relation of the Same Entity Type

JPA: How to have one-to-many relation of the same Entity type

Yes, this is possible. This is a special case of the standard bidirectional @ManyToOne/@OneToMany relationship. It is special because the entity on each end of the relationship is the same. The general case is detailed in Section 2.10.2 of the JPA 2.0 spec.

Here's a worked example. First, the entity class A:

@Entity
public class A implements Serializable {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private A parent;
@OneToMany(mappedBy="parent")
private Collection<A> children;

// Getters, Setters, serialVersionUID, etc...
}

Here's a rough main() method that persists three such entities:

public static void main(String[] args) {

EntityManager em = ... // from EntityManagerFactory, injection, etc.

em.getTransaction().begin();

A parent = new A();
A son = new A();
A daughter = new A();

son.setParent(parent);
daughter.setParent(parent);
parent.setChildren(Arrays.asList(son, daughter));

em.persist(parent);
em.persist(son);
em.persist(daughter);

em.getTransaction().commit();
}

In this case, all three entity instances must be persisted before transaction commit. If I fail to persist one of the entities in the graph of parent-child relationships, then an exception is thrown on commit(). On Eclipselink, this is a RollbackException detailing the inconsistency.

This behavior is configurable through the cascade attribute on A's @OneToMany and @ManyToOne annotations. For instance, if I set cascade=CascadeType.ALL on both of those annotations, I could safely persist one of the entities and ignore the others. Say I persisted parent in my transaction. The JPA implementation traverses parent's children property because it is marked with CascadeType.ALL. The JPA implementation finds son and daughter there. It then persists both children on my behalf, even though I didn't explicitly request it.

One more note. It is always the programmer's responsibility to update both sides of a bidirectional relationship. In other words, whenever I add a child to some parent, I must update the child's parent property accordingly. Updating only one side of a bidirectional relationship is an error under JPA. Always update both sides of the relationship. This is written unambiguously on page 42 of the JPA 2.0 spec:

Note that it is the application that bears responsibility for maintaining the consistency of runtime relationships—for example, for insuring that the “one” and the “many” sides of a bidirectional relationship are consistent with one another when the application updates the
relationship at runtime.

JPA onetomany on same entity

Assuming you have two tables, Person and Person_Friends. The Person class looks as below:

NOTE: To keep it simple, I have used IDENTITY as GenerationType and int as datatype for id.

@Entity
class Person
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name="Person_Friends")
List<Person> friends = new ArrayList<>();

@Override
public String toString() {
return "Person [id=" + id + ", friends=" + friends + "]";
}
}

The code to save a sample Person object with friends:

entityManager.getTransaction().begin();
Person p = new Person();
p.friends.add(new Person());
p.friends.add(new Person());
p.friends.add(new Person());
p.friends.add(new Person());
entityManager.persist(p);
entityManager.getTransaction().commit();

Not able to figure out is the friend data actually getting stored ?

With this schema you should be able to find friends data in Person_Friends table.

If yes , how to access it ?

Loading the Person object for whom you want to view the friends data should populate the friends list as well although lazily for this mapping.

In case you want to see the auto-generated tables used here, the DDLs below:

    create table Person (
id integer generated by default as identity,
primary key (id)
)

create table Person_Friends (
Person_id integer not null,
friends_id integer not null
)

alter table Person_Friends
add constraint UK_4ehyhs34ebu5gl6k8u5ckd2u7 unique (friends_id)

alter table Person_Friends
add constraint FKjvrny03ut8h70garyw5ehnlr7
foreign key (friends_id)
references Person

alter table Person_Friends
add constraint FK85ngln3801hk33fkhhsl7b8e7
foreign key (Person_id)
references Person

JPA Multiple OneToMany relations to the same Entity

You should have two references to the 'Folder' in your AbstractItem, for each case. Thus, mappedBy values should be specified accordingly, e.g.:

  @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private List<AbstractItem> items = new LinkedList<AbstractItem>();

@OneToMany(mappedBy = "parent2", cascade = CascadeType.PERSIST)
private List<AbstractItem> items2 = new LinkedList<AbstractItem>();

Multiple one-to-many relationships to same entity type and table?

Ideally you should have a "type" or "flag" column in SHELF_BOOK table indicating the book is fiction or non-fiction.

Suppose you have added this "type" column, then I think you could specify a filter statement in the set:

<set name="fictionBooks" table="SHELF_BOOK" cascade="all-delete-orphan"   lazy="false">
<filter name="myfilter" condition=":type = 'FICTION'"/>
<key column="bookShelfId" />
<one-to-many class="com.example.Book" />
</set>

You can refer to http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#objectstate-filters

Modelling a series of elements of the same entity in JPA (one-to-one relation)

Try this way:

@OneToOne
@JoinColumn
private Book previousPart;

In your code you defined the book_id field as the join-column, which is an already existing column (the id itself), so actually you linked all entries to themselves.

One to Many Relationship with same Entity

You get the parent from the database, do the attachments, and persist the two children:

A parent = em.get(A.class, parentId);
A son = new A();
A daughter = new A();
son.setParentActivity(parent);
daughter.setParentActivity(parent);
em.persist(son);
em.persist(daughter);
parent.getSubActivities().add(son);
parent.getSubActivities().add(daughter);

JPA - Mapping a column with same entity

No problem. It is very common to have this type of self referencing relationship especially when dealing with the hierarchical data.

In the OOP mindset , we would like to work on the abstraction rather than detail as it is easy to understand. So it is even a good parasite if apply this pattern correctly provided that the parent and children entities are really the same abstraction which has some common behaviour in nature . Modelling the parent and the children as the same type allow us to treat them as the same abstraction when writing the logic on them.

If parent is set for same row i.e parent indicates same row, then
would it be a loop?

Yes. It will form a loop.That 's why when setting a parent for a children , you should check that the parent cannot be the children itself which should be regarded as one of the business requirements that should be enforced in codes.

Many to many relationship for same type entity

// Creating a graph to help hibernate to create a query with outer join.
@NamedEntityGraph(name="graph.Person.friends",
attributeNodes = @NamedAttributeNode(value = "friends"))
class Person {}

interface PersonRepository extends JpaRepository<Person, Long> {
// using the named graph, it will fetch all friends in same query
@Override
@EntityGraph(value="graph.Person.friends")
Person findOne(Long id);
}

@Override
public Person addFriend(String personName, String friendName)
throws FriendshipExistsException, PersonNotFoundException {
Person person = retrieveWithName(personName);
Person friend = retrieveWithName(friendName);
if(!person.getFriends().contains(friend)){
person.getFriends().add(friend);
friend.getFriends().add(person); // need to setup the relation
return repository.save(person); // only one save method is used, it saves friends with cascade
} else {
throw new FriendshipExistsException(personName, friendName);
}

}

If you check your hibernate logs, you will see:

Hibernate: insert into person (name, id) values (?, ?)

Hibernate: insert into person (name, id) values (?, ?)

Hibernate: insert into friendship (person_id, friend_id) values (?, ?)

Hibernate: insert into friendship (person_id, friend_id) values (?, ?)



Related Topics



Leave a reply



Submit