Eines meiner momentanen Projekte verlangt von mir, dass eine Java-Klasse über zwei Relationen mit sich selbst verbunden ist. Ich versuche mal diese abstrakte Anforderung am Beispiel von Twitter zu schildern: Ein Benutzer (Klasse/Entität) hat auf der linken Seite die Personen, denen er folgt (Following) und auf der rechten Seite die Personen, die ihm folgen (Followers). Jede Entität kann untereinander mehrmals verbunden sein.

Um solch eine Datenstruktur mit Hilfe von JPA zu realisieren, braucht man zwei Klassen. Die erste Klasse realisiert den Benutzer (User), die zweite Klasse (FollowerFollowingAssociation) stellt die Verbindung zwischen zwei Benutzern her.

In der Klasse User wird über zwei OneToMany-Annotations die Verbindung zu der Assoziation hergestellt:

package de.ckl.test.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;

@Entity
@Table("user")
public class User
{
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private long id;

	@OneToMany(mappedBy = "following")
	private List<FollowerFollowingAssociation> hasManyFollowing;

	@OneToMany(mappedBy = "follower")
	private List<FollowerFollowingAssociation> hasManyFollowers;

	public void setId(long id)
	{
		this.id = id;
	}

	public long getId()
	{
		return id;
	}

	public void setFollowers(List<FollowerFollowingAssociation> _followers)
	{
		this.hasManyFollowers = _followers;
	}

	public List<FollowerFollowingAssociation> getFollowers()
	{
		return hasManyFollowers;
	}

	public void setFollowing(List<FollowerFollowingAssociation> _following)
	{
		this.hasManyFollowing = _following;
	}

	public List<FollowerFollowingAssociation> getFollowing()
	{
		return hasManyFollowing;
	}

	/**
	 * Führt dazu, dass ein Benutzer einem anderen User folgt
	 *
	 * @param _user
	 * @param _isConfirmed
	 */
	public void follow(User _userToFollow, boolean _isConfirmed)
	{
		FollowerFollowingAssociation association = new FollowerFollowingAssociation();
		association.setFollowing(_userToFollow);
		association.setFollower(this);
		association.setConfirmed(_isConfirmed);
		getFollowing().add(association);
		_userToFollow.getFollowers().add(association);
	}
}

Die FollowerFollowingAssociation mappt wiederum über zwei ManyToOne-Annotations zur User-Klasse:

package de.ckl.test.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "follower_following_association")
public class FollowerFollowingAssociation
{
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private long id;

	public void setId(long id)
	{
		this.id = id;
	}

	public long getId()
	{
		return id;
	}

	@Column(name = "is_confirmed")
	private boolean isConfirmed;

	@ManyToOne
	@JoinColumn(name = "id_follower")
	private User follower;

	@ManyToOne
	@JoinColumn(name = "id_following")
	private User following;

	public void setConfirmed(boolean isConfirmed)
	{
		this.isConfirmed = isConfirmed;
	}

	public boolean isConfirmed()
	{
		return isConfirmed;
	}

	public void setFollowing(User _following)
	{
		this.following = _following;
	}

	public User getFollowing()
	{
		return following;
	}

	public void setFollower(User _follower)
	{
		this.follower = _follower;
	}

	public User getFollower()
	{
		return follower;
	}

}

Das obige Beispiel führt dann dazu, dass einmal die Tabelle user mit dem Feld id und andererseits die Tabelle follower_following_association mit der Struktur id int, follower_id int (FK user(id)), following_id int (FK user(id)), is_confirmed boolean erstellt wird.

Für genau solch eine Anforderung wäre natürlich eine Datenbank wie GraphDB eher geeignet, aber diese mit JPA anzusprechen ist (momentan noch) nicht möglich.

I am asking you for a donation.

You liked the content or this article has helped and reduced the amount of time you have struggled with this issue? Please donate a few bucks so I can keep going with solving challenges.