Embedding Generic Types in JPA - java

Does JPA support embedding a class attribute whose type is a parameterized generic or java.lang.Object? For example:
public class Foo<T>;
{
private T param1;
private Object param2;
}
I have a use case where I have a class that "wraps" some arbitrary class (the generic T or java.lang.Object) via aggregation plus contains primitive types representing metadata about the wrapped object.
In this case, I'd like there to be DB tables for each of things being wrapped that also contain columns for the metadata. These metadata columns would be duplicated across all tables representing the wrapped embedded objects.

I'd like there to be DB tables for each of things being wrapped that also contain columns for the metadata. These metadata columns would be duplicated across all tables representing the wrapped embedded objects.
You could maybe persist Object or T as a #Lob but I don't think that the above is possible, I don't see how you could express the mappings, how a JPA provider could be aware of the tables.
See also
Enterprise JavaBeans - JPA Generic entities classes Mappedsuperclass are not possible!

Related

Hibernate Embeddable: validate on post load

I have an #Embeddable class with two fields: type and value. Both fields map to the corresponding database columns. The first one is enum and the latter one is an interface that has multiple implementations.
Only certain combinations of type and value are considered valid even if type and value are correct in isolation. How can I perform such validation, when I retrieve the entity that owns the #Embeddable from the database?
I could perform validation inside no-args-constructor of embeddable, but as far as I'm concerned, Hibernate creates new #Embeddable instance with no-args-constructor and then injects the values with Java Reflection API. Therefore, if I access these two fields inside the constructor they will be null.
Is there an approach to register some PostLoad hook for the #Embeddable classes that Hibernate will trigger? I though about declaring PostLoad inside the entity itself and then calling MyEmbeddable.validate directly. But I think it's a dirty approach.
I added the class-level annotation to validate the whole object. It did work. Check out this question for more details.

Difference JPA/Hibernate persists fields of type List<String> vs List<MyPersonEntity> vs List<NonEntityClass>

I know we persist collections (List, Set, Map, Collection) in two ways - annotate collection field with some JPA (javax.persistence) relation mapping annotation (#OneToMany, #ManyToMany) or use #ElementCollection JPA annotation.
1). Does JPA (or Hibernate underlying JPA) without above #OneToMany/#ElementCollection annotations persist List, Set, Map, Collection fields (fields not annotated with anything, their class is #Entity and #Id + #GeneratedValue present - and no more annotations).
Any primitive, enum and Serializable fields shall be persisted (as per JPA) - so collection fields shall also be persisted and then retrieved with entityManager.find - right? OK, order not preserved so I need to use #OrderColumn or #OrderBy JPA annotation (or Hibernate-specific deprecated #Sort or recommended now #SortNatural or #SortComparator ) additionally.
So my guess is that List<String> and List<MyPersonEntity> would be persisted and retrieved (as a field) without any annotations, but I'm not sure about List<MyNonEntityClass> (I think same goes for it).
What is the differnrece in persisting (allowed annotations, what can be persisted, what can be persisted as field without any annotations) List<String> vs List<MyPersonEntity> vs List<SomeNonEntity> exactly? Is there any such difference?
I googled a lot, read a few manuals, but found no direct answers.
Thorben Janssen writes
An #ElementCollection enables you to map a Collection of values **that are not an entity itself. **
It implies to me that List<MyPersonEntity> is persisted to DB without any annotations on List field, but List<OrderNotEntity> needs either #ElementCollection or #OneToMany/#ManyToMany annotation to be persisted. Does it throw an error or just silently fails to persist and then resolves to null when retrieved from database with entityManager.find?
[This][1] implies that #ElementCollection is needed only for List<BasicType> (List<String>, List<Integer>, etc) and List<Embeddable>, but not List<EntityClass>.
3). Can I use #ElementCollection with List<SerializableNonEntityClass> (any Serializable class is Basic, right?), List<MyEnumColor> (all enums are serializable), List<java.util.Date>, List<java.sql.Time> (also basic type), List<Set ?
JPA [Specification][2] does not define term "basic type", but provides a list of what can be annotated with #Basic:
11.1.6 Basic Annotation The Basic annotation is the simplest type of mapping to a database column. The Basic annotation can be applied to a
persistent property or instance variable of any of the following
types: Java primitive types, wrappers of the primitive types,
java.lang.String, java.math.BigInteger, java.math.BigDecimal,
java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time,
java.sql.Timestamp, byte[], Byte[], char[], Character[], enums, and
any other type that implements Serializable. As described in Section
2.8, the use of the Basic annotation is optional for persistent fields and properties of these types. If the Basic annotation is not
specified for such a field or property, the default values of the
Basic annotation will apply
2.8 Mapping Defaults for Non-Relationship Fields or Properties If a persistent field or property other than a relationship property is not
annotated with one of the mapping annotations defined in Chapter 11
(or equivalent mapping information is not specified in the XML
descriptor), the following default mapping rules are applied in order:
• If the type is a class that is annotated with the Embeddable
annotation, it is mapped in the same way as if the field or property
were annotated with the Embedded annotation. See Sections 11.1.15 and
11.1.16. • If the type of the field or property is one of the following, it is mapped in the same way as it would if it were
annotated as Basic: Java primitive types, wrappers of the primitive
types, java.lang.String, java.math.BigInteger, java.math.BigDecimal,
java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time,
java.sql.Timestamp, byte[], Byte[], char[], Character[], enums, any
other type that implements Serializable.
I don't understand that reference to section 2.8. So if I don't use #ElementCollection - no problem, mapping to DB also succeeds and everything is persisted just fine???
[1]: https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#:~:text=It%20is%20meant%20to%20handle,a%20collection%20of%20String%20s)
[2]: https://download.oracle.com/otn-pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf?AuthParam=1648386141_7549cef1d21f5581b4966d8d8ef70de4

JPA mapping annotations for Object type

I will implement an entity class, something like:
#Entity
public class XXXEntity {
#Id
private Long id;
private Object entity;
}
But I am not sure how to map the Object type field?
Actually, in my project the object types contain several other entities, like A, B, C. (all A,B, and C implement Serilizable interface)
I want to annotate it as #Lob, but I am not sure it is correct? Because Object doesn't implement Serilizable interface.
Any ideas about that? Thanks.
You can not annotate Object field as #Lob. According to spec: "A Lob may be either a binary or character type."
You should decide whether A, B and C are entities or state field values. If they are entities and they can be referenced, queried by their properties, create a common super class a go with the inheritance.
If they are just an object then using Lob and implementing Serializable should suffice. If the JPA provider requires you to replace Object with something else, as you have stated you can use Serializable interface, if that does not work then a common super class which implements Serializable.
It seems to me you want to use it as "ValueObject" (as u mentioned serializable) instead of "Entity".
It is fine if you treat the field as ValueObject. Using Lob should be fine (and you can always find workaround by manually serializing it).
But if you want to treat them as "Entity ", it will never be reasonable to declare it as ref to "Object". One of the most obvious argument is Object is NOT an Entity, as it doesn't contains any entity identity.
No, it is not correct to annotate it with #Lob. You cannot directly annotate arbitrary Object as a persistent attribute. JPA provider have no way to know how to persist it.
According JPA 2.0 specification persistent attribute must be one of following:
Java primitive types; java.lang.String; other Java serializable types
(including wrappers of the primitive types,
java.math.BigInteger,java.math.BigDecimal,java.util.Date,java.util.Calendar[5],
java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], Byte[],
char[], Character[], and user-defined types that implement the
Serializable interface); enums; entity types; collections of entity
types; embeddable classes; collections of basic and embeddable types.
You should redesign model such a way that there is no need to persist basically typeless data.

Is nesting of different types of inheritance allowed in hibernate.?

I have a situation like this...
I have four different tables namely btech_ece, btech_cse, btech_it, btech_eee all these tables have the same columns. Its just that the name differs and everything else is same. Their columns are:
Id
Year
Semister
Section
Period
SubjectCode
Date
Status
now i have four different tables under each division ece, cse, it and eee. btech_cse_1, bteh_cse_2, btech_cse_3, btech_cse_4, btech_ece_1, btech_ece_2 and so on..
the columns of these tables are like this:
Roll_Number
Id
Present
the id column above is a foreign key from the tables btech_cse. My idea was that if i can use a nesting of hibernate's inheritance hierarchy. I could have a class named btech and all the classes btech_cse, btech_ece, btech_eee, btech_it can be its union-subclasses and i can have another class that says btech_cse_num which is a joined subclass of the above union subclass and each class of the form btech_cse_1 could be again a union-subclass of the btech_cse_num class. Is such nesting allowed in hibernate?
How do i best exploit the hibernate's inheritance feature to suit my situation.?
If feasibility is the only concern, then I suggest you look at the hibernate reference documentation. The chapter on "Inheritance Mapping" gives some useful pointers, this is from version 3.3 docs:
Hibernate does not support mixing subclass, joined-subclass and union-subclass mappings under the same root class element. It is possible to mix together the table per hierarchy and table per subclass strategies under the the same class element, by combining the subclass and join elements. It is possible to define subclass, union-subclass, and joined-subclass mappings in separate mapping documents directly beneath hibernate-mapping. This allows you to extend a class hierarchy by adding a new mapping file.

Persisting Serializable Objects in Hibernate

I am attempting to persist objects that contain some large Serializable types. I want Hibernate to automatically generate my DDL (using Hibernate annotations). For the most part, this works, but the default database column type used by Hibernate when persisting these types is tinyblob. Unfortunately, this causes crashes when attempting to persist my class, because these types will not fit within the length of tinyblob.
However, if I manually set the type (using #Column(columnDefinition="longblob"), or more portably #Column(length=500000)), it works fine. Is there any way to make the default binary type longblob instead of tinyblob, so that I don't need to manually specify the #Column annotation on each field?
ExampleClass.java:
public class ExampleClass
{
#Column(columnDefinition="longblob")
ExampleSerializable ser1;
#Column(columnDefinition="longblob")
ExampleSerializable ser2;
...
}
ExampleSerializable.java:
public class ExampleSerializable implements java.io.Serializable
{
// MANY Fields
}
EDIT
Since there seems to be some confusion: annotating each field with #Column(columnDefinition="longblob") (or more portably: #Column(length=500000)), already works. I am looking for a solution that does not require me to annotate each and every field.
I think (didn't test it) that Hibernate will generate a tinyblob, blob, mediumblob column depending on the column length (respectively 255, 65535, 16777215) which defaults to 255. I would try to specify it (and this would be a portable solution).
Now, if really you want to force things, you'll have to extend the MySQL dialect (but this will harm portability).

Categories

Resources