Question

I have the following class

public class ElementBean {
 private String link;
 private Set<ElementBean> connections;
}

I need to create a map table where elements are mapped to each other in a many-to-many symmetrical relationship.

@ManyToMany(targetEntity=ElementBean.class)
@JoinTable(
    name="element_elements",
    joinColumns=@JoinColumn(name="FROM_ELEMENT_ID", nullable=false),
    inverseJoinColumns=@JoinColumn(name="TO_ELEMENT_ID", nullable=false)
)
public Set<ElementBean> getConnections() {
    return connections;
}

I have the following requirements

When element A is added as a connection to Element B, then element B should become a connection of Element A. So A.getConnections() should return B and B.getConnections() should return A. I do not want to explicitly create 2 records one for mapping A to B and another for B to A.

Is this possible?

UPDATE : Thank you for all the suggestions.
When I try @Pascal's suggestion two records are created as follows when I try to connect element 1 with element 2.

FROM_ELEMENT_ID, TO_ELEMENT_ID
1, 2
2, 1

I want the records to be symmetrical where 1,2 is the same as 2,1

How is this issue dealt with?

Update I explicitly created a map bean

class ConnectionBean {
    ElementBean from;
    ElementBean to;
}

@NotNull
@ManyToOne
@JoinColumn(name="FROM_ELEMENT_ID", nullable=false)
public ElementBean getFrom() {
    return from;
}

@NotNull
@ManyToOne
@JoinColumn(name="TO_ELEMENT_ID", nullable=false)
public ElementBean getTo() {
    return to;
}

I updated the ElementBean to

public class ElementBean {
    private String link;
    private Set<ConnectionBean> from;
    private Set<ConnectionBean> to;
}

@OneToMany(mappedBy="from", fetch=FetchType.LAZY)
public Set<ConnectionBean> getFrom() {
    return from;
}

@OneToMany(mappedBy="to", fetch=FetchType.LAZY)
public Set<ConnectionBean> getTo() {
    return to;
}

Here I can control the insertion and deletion. For example before inserting a new ConnectionBean, I check to see if a connection exists between elements A & B by checking the Connection table for records where
((from == A && to == B) || (from == B && to == A))
before insertion.

Was it helpful?

Solution

When working with many to many, you don't have to worry too much about duplication as Hibernate will handle that for you. You do have to worry about making sure your object model is consistent and Hibernate won't necessarily help with that.

In your addConnection() method you should not have a getConnections() method that returns the actual connection, rather return a read only version, you'll need to make sure you handle both sides of the relationship:

addConnection(ElementBean element) {
    if (element ==null) { 
        throw new IllegalArgumentException("cannot add null element"); 
    }
    connections.add(element);
    element.getConnections0().add(this);
}

This means your requirement will hold and when it saves it will save okay. It will not save twice as it will notice that the one is a back reference for the other. You should probably make the getConnections0() private or package.

OTHER TIPS

This will be solved by hibernate's first level cache to keep track of the references as long as you map the m-2-m from both sides.

When working with bi-directional links, you need to take care of the link on both sides and, quoting the documentation:

Many developers program defensive and create a link management methods to correctly set both sides.

So in your case, I'd suggest to add the following methods:

public Set<ElementBean> getConnections() {
    return connections;
}

public void setConnections(Set<ElementBean> connections) {
    this.connections = connections;
}

public void addToConnection(ElementBean connection) {
    this.getConnections().add(connection);
    connection.getConnections().add(this);
}

public void removeFromConnection(ElementBean connection) {
    this.getConnections().remove(connection);
    connection.getConnections().remove(this);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top