I'm working on a JPA project. I have an ExportProfile object:
#Entity
public class ExportProfile{
#Id
#GeneratedValue
private int id;
private String name;
private ExtractionType type;
//...
}
ExtractionType is an interface implemented by several classes, each for a different extraction type, these classes are singletons.
So type is a reference to a singleton object. I don't have an ExtractionType table in my DB, but i have to persist the extraction type of my export profile.
How can I persist the ExportProfile object using JPA, saving the reference to type object?
NOTE: The number of ExtractionType implementations is not defined, because new implementation can be added anytime. I'm also using Spring, can this help?
Here's an idea: make an ExtractionTypeEnum, an enumeration with one element for each of the possible singletons that implement ExtractionType, and store it as a field in your entity, instead of ExtractionType . Later on, if you need to to retrieve the singleton corresponding to a ExtractionTypeEnum value, you can implement a factory that returns the correct singleton for each case:
public ExtractionType getType(ExportProfile profile) {
switch (profile.getExtractionTypeEnum()) {
case ExtractionTypeEnum.TYPE1:
return ConcreteExtractionType1.getInstance();
case ExtractionTypeEnum.TYPE2:
return ConcreteExtractionType2.getInstance();
}
}
In the above, I'm assuming that both ConcreteExtractionType1 and ConcreteExtractionType2 implement ExtractionType.
Related
Say I have the following Java class, which is owned by a vendor so I can't change it:
public class Entry {
private String user;
private String city;
// ...
// About 10 other fields
// ...
// Getters, setters, etc.
}
I would like to persist it to a table, using JPA 2.0 (OpenJPA implementation). I cannot annotate this class (as it is not mine), so I'm using orm.xml to do that.
I'm creating a table containing a column per field, plus another column called ID. Then, I'm creating a sequence for it.
My question is: is it at all possible to tell JPA that the ID that I would like to use for this entity doesn't even exist as a member attribute in the Entry class? How do I go about creating a JPA entity that will allow me to persist instances of this class?
EDIT
I am aware of the strategy of extending the class and adding an ID property it. However, I'm looking for a solution that doesn't involve extending this class, because I need this solution to also be applicable for the case when it's not only one class that I have to persist, but a collection of interlinked classes - none of which has any ID property. In such a scenario, extending doesn't work out.
Eventually, I ended up doing the following:
public class EntryWrapper {
#Id
private long id;
#Embedded
private Entry entry;
}
So, I am indeed wrapping the entity but differently from the way that had been suggested. As the Entry class is vendor-provided, I did all its ORM work in an orm.xml file. When persisting, I persist EntryWrapper.
I don't have much experience with JPA, but I wouldn't extend your base classes, instead I would wrap them:
public class PersistMe<T> {
#Id
private long id;
private T objToWrap;
public(T objToWrap) {
this.objToWrap = objToWrap;
}
}
I can't test it, if it doesn't work let me know so I can delete the answer.
How can I create collection OneToMany without using annotations?
Now I'm using DatabaseFieldConfig like this:
DatabaseFieldConfig collection = new DatabaseFieldConfig("name");
collection.setForeignCollection(true);
But it doesn't work because I always get "Not fields has DatabaseField annotation" when I'm trying to create Dao.
Below is my example code:
public class HasCollection {
private long id;
private Collection<CollElement> coll;
...
}
public class CollElement {
private long id;
private HasCollection parent;
...
}
When I'm creating DAO for HasCollection I get error that "Not fields has DatabaseField annotation in CollElement" and if I'm creating DAO for CollElement I get error "Not fields has DatabaseField annotation in HasCollection"
I made a sample code when extending class but it would drag dependency on ormlite DAO into this extended model class which is definitly not the right pattern.
Therefore in this case I would consider the class from another project as a DTO (data transfer object) and I would create another model class with annotations for ormlite.
Than made a simple convertor from DTO to the model class and vice versa used in layer where you want to persist objects.
Right now I have a class BaseSchedule It is used by 4 classes (composition). I would like to validate in two use classes and not in the others. I am a little stumped on how to do so.
My BaseSchedule looks like the following:
#Embeddable
#DatesStartBeforeEnd(start = "startDateTime", end = "endDateTime")
public class BaseSchedule implements Serializable {
private Date startDateTime;
private Date endDateTime;
}
I would like to check to make sure that the startDateTime and endDateTime are not null when I go to persist the data to my database. Normally I would provide a #NotNull to each of the fields.
public class TimeSlot implements Scheduleable {
#Embedded
private BaseSchedule schedule;
}
But... in the case of my TimeSlotTemplate I do not want validation as I know it will be null.
public class TimeSlotTemplate extends SchedulableClassTemplateEvent {
#Embedded
private BaseSchedule schedule;
}
If you're using Hibernate Validator as your BV provider, one solution might be to use a custom default group sequence provider.
For this to work, your BaseSchedule object would have to know about the "role" it currently has, e.g. by passing an enum with values such as SlotSchedule, TemplateSchedule etc. to its constructor. Depending on the role a group sequence provider could then determine the sequence to validate and return a sequence which does not contain the #NotNull constraints if the role is TemplateSchedule.
Not that this approach requires that you use the default sequence during JPA lifecycle validation.
I think this could be done using the #PrePersist Annotation.
#PrePersist
public void prePersist(){
if(isPerformNullableCheck()){
// check for null values and raise an error if invalid
}
}
in your TimeSlotTemplate, you can set the performNullableCheck property to false...
Another way might be to add a class into the hierarchy.
BaseSchedule contains the properties needed and e.g. ValidatedSchedule (extends BaseSchedule) overrides those and performs the notnull checks. Don't know whether this works or not. Also this would probably not be the best solution for your problem..?
Question:
Is it possible to have a field persisted by JPA but skipped by serialization?
It is possible to achive the opposite (JPA skips a field and serialization doesn't), and if this feature is used, surely the reverse would be useful.
Something like this:
#Entity
class MyClass {
// Other fields.
#NonTransient
private transient String strangeField;
}
I am asking mostly out of curiosity, so I don't have a specific context.
One option is to use property access on the entity. Then, mark the field as transient. JPA will ignore the field and only use the getter. Thus, serialization skips the field, and JPA uses the getter.
#Entity(AccessType.Property)
class MyClass {
// Other fields.
private transient String strangeField;
public String getStrangeField() {
return strangeField;
}
public void setStrangeField(String strangeField) {
this.strangeField = strangeField;
}
}
You need to use property access, or use XML to map the entity instead of annotations.
I'm observing a very strange behaviour with an entity class and loading an object of this class whith JPA (hibernate entitymanager 3.3.1.ga). The Class has a (embedded) field, that is initialized in the declaration. The setter of the field implements a null check (i.e. would throw an exception when a null value is set).
...
#Entity
public class Participant extends BaseEntity implements Comparable<Participant> {
...
#Embedded
private AmsData amsData = new AmsData();
public void setAmsData(AmsData amsData) {
Checks.verifyArgNotNull(amsData, "amsdata");
this.amsData = amsData;
}
...
}
When I get this object with JPA, the field is null, if there is no data in the db for the fields specified in the embedded object.
...
public class ParticipantJpaDao implements ParticipantDao {
#PersistenceContext
private EntityManager em;
#Override
public Participant getParticipant(Long id) {
return em.find(Participant.class, id);
}
...
}
I debugged the process with a watchpoint on the field (should halt when the field is accessed or modified), and I see one modification when the field is initialized, but when I get the result from the find call, the field is null.
Can anybody explain, why this is so? How can I ensure, that the field is not null, also when there is no data for the embedded object's fields in the db (besides from setting it manually after the find call).
The JPA specification doesn't explicitly say how to handle a set of columns representing an embeddable object which are all empty. It could signal a null reference, or an object instance with all null fields. Hibernate chooses a null reference in this case, though other JPA implementations may pick the later.
The reason why your setter is never called is because Hibernate is accessing your field via reflection, bypassing the setter you implemented. It's doing this because you utilize field-based access rather than property-based access.
Chad's answer would provide the functionality you're looking for, but there is a caveat (see below).
"...The persistent state of an entity
is accessed by the persistence
provider runtime[1] either via
JavaBeans style property accessors or
via instance variables. A single
access type (field or property access)
applies to an entity hierarchy. When
annotations are used, the placement of
the mapping annotations on either the
persistent fields or persistent
properties of the entity class
specifies the access type as being
either field- or property-based access
respectively..." [ejb3 persistence
spec]
so by moving the annotations down to the setter, you are telling JPA that you want to used property-based access instead of field-based access. You should know, however, that field-based access - as you currently implement it - is preferred over property-based access. There are a couple reasons why property-based access is discouraged, but one is that they you're forced to add getters and setters for all of your persistent entity fields, but you may not want those same fields susceptible to mutation by external clients. In other words, using JPA's property-based access forces you to weaken your entity's encapsulation.
The answer is (thanks to rcampell), if all data of an embedded object is null (in the db), the embedded object will also be null, although when it is initialized in the declaration. The only solution seems to be, setting the object manually.
#Override
public Participant getParticipant(Long id) {
Participant participant = em.find(Participant.class, id);
if(participant != null && participant.getAmsData() == null)
{
participant.setAmsData(new AmsData());
}
return participant;
}
Still feels strange to me ...
Well, it's possible that your object could be getting constructed twice behind the scenes. JPA implementations will usually set those fields directly.
I think you need to put the annotations on the Getters and setters themselves if you want them to be used. See this answer:
Empty constructors and setters on JPA Entites
It's 2018 now and I had the same problem in a similiar situation.
Using your code as example, I solved the problem like this:
#Entity
public class Participant extends BaseEntity implements Comparable<Participant> {
...
#Embedded
private AmsData amsData = new AmsData();
public void getAmsData(AmsData amsData) {
Checks.verifyArgNotNull(amsData, "amsdata");
this.amsData = amsData;
}
public AmsData getAmsData(){
if(amsData == null){
amsData = new AmsData();
}
return amsData;
}
...
}
I was having the same problem , I just added getters and setters using #Getter and #setter lombok annotations and it started working