I'm having some trouble trying to figure out how to set up a class that has an #Embedded field that must be fetched lazily. I tried to annotate the field with #Basic(fetch = FetchType.LAZY), but it causes the persistence API to treat the field as a basic type that implements Serializable, so it maps the field to a BYTEA field in the database (postgresql). I tested it on Derby too, and the same happens.
I also tried to annotate the fields of the #Embeddable class individually with #Basic(fetch = FetchType.LAZY) instead of annotating the #Embedded field of the entity that has it. The generated schema is correct in this case, but the fields are fetched eagerly when I load instances of the entity.
My understanding is that the #Basic annotation is used on basic fields/properties only, so the first case is expected. But why the fields of the #Embeddable class are fetched eagerly even if they are annotated with #Basic(fetch = FetchType.LAZY)? Also, I know that the fetch strategy can be specified by the #Basic and relationship annotations, but is there any other way to specify that fields should be fetched lazily? I'm using EclipseLink 2.6, but let me know if the behaviour is different for other versions of EclipseLink or for another provider.
Directly you cant, because of how #Embedded objects work, but by setting attributes in the object it should work.
#Basic(fetch=FetchType.LAZY)
Remember that lazy should be use only on collections or big objects, and that setting fetch type on lazy is only a clue for provider, it doesn't mean that it will always fetch it lazy rather than eager.
Situation
I have an Entity with a DiscriminatorColumn, configured for single table inheritance:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public class ContainerAssignment{
...
}
'ContainerAssignment' has a reference to another Entity:
#JoinColumn(name="CONTAINER_ID")
private Container container;
A container may have one ContainerAssignment of each TYPE. This means that the primary key of the ContainerAssignment table is defined by the CONTAINER_ID and the TYPE.
ContainerAssignment has some subclasses e.g.
#Entity
#DiscriminatorValue("SOME_TYPE")
public class SomeTypeOfContainerAssignment extends ContainerAssignment{
...
}
There will only be a single SomeTypeOfContainerAssignment instance for a given CONTAINER_ID.
Problem
If I define the JPA #Id as just the Container on the ContainerAssignment table, I can do entityManager.find(SomeTypeOfContainerAssignment.class, containerId), which is great. This runs something along the lines of SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1 AND TYPE = 'SOME_TYPE';. It knows it needs the TYPE check in here, because of the #DiscriminatorValue("SOME_TYPE") annotation on the Entity.
However, this means that the back references from Container to ContainerAssignment breaks as Container is not really the primary key. For example, if Container has a #OneToOne(mappedBy=container) private SomeTypeOfContainerAssignment assignment;, when you read in a container, it will read in the assignment by something like SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1;, without the type checking. This gives it all assignments for a container, and then it picks one seemingly at random, potentially of the wrong type, in which case, it throws an exception.
If instead, I define the JPA #Id of ContainerAssignment as a composite id using container and type, references to the sub-classes of ContainerAssignment work fine.
However, I cannot do entityManager.find(SomeTypeOfContainerAssignment.class, containerId), because containerId is not the id. I have to do entityManager.find(SomeTypeOfContainerAssignment.class, new MyPk(containerId, "SOME_TYPE")), which seems to defeate the point of #DiscriminatorValue("SOME_TYPE"). I might as well just use a single ContainerAssignment Entity if I have to specify type on find anyway.
Question
Is there a way to have working references to sub-classes of a single table inheritance Entity where the primary key on the table is composite on the discriminator column, whilst also being able to EntityManager.find by just the part(s) of the primary key which are not the discriminator?
I´m going to assume that the composite primary key of ContainerAssignment is working fine (I really think it may be JPA implementation dependent!), and all that still bothers you is the annoying call to the entityManager.find and PK instantiation.
My solution is to define finder methods independent of the JPA API. Don´t lock yourself to JPA.
The simplest way is to just define a static finder at your domain class (or, define another class with just finders, if you want to keep domain uncoupled do JPA. Dig at IoC to know how to do that).
At ContainerAssignment (or your finder class):
public static <T extends ContainerAssignment> T findByPK(EntityManager manager,Class<T> type,long id) {
DiscriminatorValue val = type.getAnnotation(DiscriminatorValue.class); // this is not optimal...can be cached...
return (T) manager.find(type, new MyPk(containerId, val.getValue()));
}
At your code:
SomeTypeOfContainerAssignment ca = ContainerAssignment.findByPK(entityManager,SomeTypeOfContainerAssignment.class,containerId);
Notice that making the type part of the PK means that you can have two ContainerAssignment instances of distinct types with the same id. You going to need a Query to retrieve ContainerAssignment if you don´t know its type. If, however, your id is generated from a sequence, you can just write another finder method that hides the inner calls to entity framework, returning the first result of the resultset.
If Container has a bidirectional OneToOne with SomeTypeOfContainerAssignment, which extends ContainerAssignment, then the container field should not be defined and mapped in ContainerAssignment, but in SomeTypeOfContainerAssignment:
public class Container {
#Id
private Long id;
#OneToOne(mappedBy = "container")
private SomeTypeOfContainerAssignment someTypeOfContainerAssignment;
}
public class ContainerAssignment {
#Id
private Long id;
}
public class SomeTypeOfContainerAssignment extends ContainerAssignment {
#OneToOne
private Container container;
}
If all the types of container assignments have such a OneToOne association with COntainer, you can define the Container as
public abstract class ContainerAssignment {
#Id
private Long id;
public abstract Container getContainer();
public abstract void setContainer(Container container);
}
To be honest, I don't know if you're allowed to use the same join column in the table to map the #OneToOne container fields of each subclass.
I think this is the best you can have. If you put the container field in the base class, then you must define the association as a OneToMany/ManyToOne association, since it's what it really is.
I don't think what you want to do is possible, and I wouldn't mess with composite PKs, as they're discouraged for good reasons, and a nightmare to use.
If you are okay with provider specific extension, Hibernate provides annotation #DiscriminatorOptions.
It helped me to solve a problem where the discriminator column is part of composite primary key.
Can I create an attribute without relation with DB Table in a Entity ?
If it can, which annotation should I use ?
Attributes that are part of a persistent entity but not intended to be persistent can either be modified
with the transient modifier in Java or be annotated with the #Transient annotation.
I need to migrate some hibernate hbm files to JPA/Hibernate annotations.
The existing relationship is as follows -
The parent class has an ID
The component class also has an ID
The 2 identifiers refer to different sequences.
I have used #Embedded and #AttributeOverride in the parent class, and #Embeddable in the component class.
Both the classes are entities.
The exception i get when i try to save a parent class object is -
org.hibernate.MappingException: component property not found: id
I suppose the exception is because i have 2 identifiers defined.
Any suggestions/workarounds on this will help greatly.
You can't make something an #Entity and #Embeddable at the same time, that makes no sense. You have to make it one or the other.
If both have an ID, and both are entities, then the Hibernate/JPA component/embeddable model doesn't apply.
i want extend an entity framework model with a temporany attribute.
I need it only in a mvc form. I don't need save it in the db.
How can i do it?
Create a partial class for the entity you want to extend
e.g.
//must be in the same namespace as the Customer entity in the model
public partial class Customer
{
public string MyProperty{get;set;}
}
This property will be unmapped and you can fill it with data after you run a query or on materialization.
OR
Create a wrapper class for your entity which expose both the unmapped property and the mapped properties the properties you need in the view.