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;
}
}