Recently, I had used http://en.wikipedia.org/wiki/Composite_pattern in one of my projects. That pattern was used on model objects that had to be persisted in database. And, not suprisingly, JPA with Hibernate as a persistence provider was used. The problem does not make any headache in its simple form, but might become complex ... (as everything in real life). Nevertheless, I decided to publish my solution to help others and give sort of baseline ...

 


Background

To refresh memory with Composite pattern: We have 3 classes (I say classes as it is not possible to map interfaces so far). First serves as a common base (interface) and gives common business methods. Second is a node in the composite structure that could contain other nodes. Third is a leaf that only implements business methods.

 

Persistable implementation

In the solution, there are couple of classes, let's start with AbstractComposite that represents common base. The code is listed bellow:

 

@Entity(name = AbstractComposite.TABLE_NAME)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "compositeType", discriminatorType = DiscriminatorType.STRING)
@NamedQueries(value = { @NamedQuery(name = "GetCompositeById", query = "from "
        + AbstractComposite.TABLE_NAME + " where id = :id") })
public abstract class AbstractComposite implements Serializable {

    private static final long serialVersionUID = 7139007918101901734L;
    public static final String TABLE_NAME = "Composite_Table";

    @Id
    @GeneratedValue
    @TableGenerator(name = "Id_Generator", table = "Sequence_Table", pkColumnName = "name", valueColumnName = "count", pkColumnValue = "Composite_Sequence")
    private Long id;

    public AbstractComposite() {
    }

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

    public Long getId() {
        return id;
    }

    // Whatever business operations come here
}

 

As the Composite structure indicates, we must cope with inheritance hierarchy. In this case, I have chosen SINGLE_TABLE because I have only small number of leaf types. In fact, each leaf type add at least one column to the table. If you are going to have rather extensive leaf types, then joining will avoid very very sparse single table.

 

Then, there is also defined DiscriminatorColumn and its type. Each leaf is the supposed to define unique DiscriminatorValue.

 

So far, so good. Let us check leaf implementation. I have created leaf that holds String value, called CompositeString. The code is listed bellow:

 

@Entity
@DiscriminatorValue("string")
public class CompositeString extends AbstractComposite {
    private static final long serialVersionUID = -7787631697361429875L;

    @Column(name = "stringValue")
    private String value;

    public CompositeString() {
    }

    public CompositeString(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return String.format("CompositeString [value = %s]", value);
    }
}

 

As I mentioned above, DiscriminatorValue defines value that will distinguish this composite type from the others. In addition, I have defined attribute value which has defined ut database name to stringValue. By redefinition of the database name using ColumnName annotation, you can have same attribute in all leaf types (check in the attached archive).

 

The last but probably the most complex is the intermediate node that must hold references to other contained nodes or leaves. Let's check the code:

 

@Entity()
@DiscriminatorValue("multi")
public class CompositeMultiValue extends AbstractComposite {

    private static final long serialVersionUID = -4324727276077386614L;

    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
    @JoinTable(name = "Composite_Multi_Values_Table", joinColumns = @JoinColumn(name = "compositeId"), inverseJoinColumns = @JoinColumn(name = "multiValueId"))
    private Set<AbstractComposite> values;

    public CompositeMultiValue() {
    }

    public CompositeMultiValue(Set<AbstractComposite> values) {
        this.values = values;
    }

    public void setValues(Set<AbstractComposite> values) {
        this.values = values;
    }

    public Set<AbstractComposite> getValues() {
        return values;
    }

    @Override
    public String toString() {
        return String.format("ComparableMultiValue [values=%s]", values);
    }
}

 

This entity again defines value that discriminates it from others. Then, it contains set of contained nodes and leaves. These are linked to the actual "multivalue" using reference table with two columns, both holding a identifier from Composite_Table. First identifier is parent (compositeId) and the second is child (multiValueId). In this particular case, the cascading is set to ALL and fetching is EAGER, but in specific cases, this might be changed appropriatelly.

 

Note on storage of enums

By default (at least in Oracle), enum values are stored as RAW values. You can observe this if you change database connection parameters to Oracle in src/test/resources/META-INF/persistence.xml. The result type for column enumValue is RAW. If you desire to store your enums as String values, you need to follow guidelines in here: http://community.jboss.org/wiki/Java5EnumUserType.

Takeaway

The codes used in this post are shipped in the attached archive as mavenized Eclipse project. There is also simple test that demonstrates usage of the composite structure.Of interrest might be after running the test also location target/hibernate3/sql/schema.ddl that contains DDL scripts for creating the database structures. Enjoy!