Are Optional ElementCollections Possible In Hibernate

168 Views Asked by At

I've noticed some strange behavior in one of my applications. I have a ManyToOne relationship between two classes, Frame and Brand. As far as the database is concerned, the relationship is unidirectional. Each Frame in the table has a brand_id column but the Brand doesn't have any columns pointing back the other way. All good so far, but in the actual code I sometimes need to get information about all the frames associated with a particular brand. To facilitate this, I added a field to the Brand class that holds a collection of frame ids and annotated it with @ElementCollection and @CollectionTable.

Here is where things get a bit funky. The Frame class is part of a hierarchy using single table inheritance. Not all of the classes that map to this table have brands, so the brand_id column has to be nullable. From what I have been able to determine so far, Hibernate forces 'nullable' to be false for relationships annotated with @ElementCollection. There doesn't seem to be any documentation explaining why. The relevant code can be found in 'org.hibernate.cfg.Ejb3JoinColumn.buildJoinTableJoinColumns()' in hibernate-core-5.3.13.Final.jar but all it says is

currentJoinColumn.setNullable( false ); //I break the spec, but it's for good

Is there any way to override this limitation in Hibernate? If not, is there some other way to do what I want without using @ElementCollection?

My Code:

@Entity(name = "Frame")
@DiscriminatorValue("FRAME")
public class Frame extends Product
{
    @ManyToOne(optional=true)
    @JoinColumn(name = "brand_id")
    @IndexedEmbedded
    protected Brand brand;

    public Brand getBrand()
    {
        return brand;
    }
}

@Entity(name = "Brand")
@Table(name = "brands")
public class Brand extends Entity
{
    @ElementCollection
    @CollectionTable(name = "products", joinColumns = @JoinColumn(name = "brand_id"))
    @Column(name = "id")
    protected Set<BigInteger> frameIds;

    public Set<BigInteger> getFrameIds()
    {
        return frameIds;
    }

    public void setFrameIds(Set<BigInteger> frameIds)
    {
        this.frameIds = frameIds;
    }
}

public class Foo extends Entity
{
    @ManyToOne
    @JoinColumn(name = "brand_id")
    protected Brand brand;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "frames_to_features", inverseJoinColumns = {
        @JoinColumn(name = "frame_id", referencedColumnName = "id") }, joinColumns = {
                @JoinColumn(name = "feature_id", referencedColumnName = "id") })
    protected Set<Frame> frames;

    public List<BigInteger> getExcludedFrameIds()
    {
        List<BigInteger> brandFrames = new ArrayList<BigInteger>();
        if (getBrand() != null)
        {
            brandFrames.addAll(getBrand().getFrameIds());
            Set<BigInteger> localFrames = getFrameIds();
            if (localFrames != null)
            {
                brandFrames.removeAll(localFrames);
            }
        }
        return brandFrames;
    }
}
0

There are 0 best solutions below