when I use the JPA #Version annotaton in an #Embeddable I get the following exception pointing at my Updateable class:
org.hibernate.AnnotationException: Unable to define #Version on an embedded class
Here is my code:
#Embeddable
public class Updateable {
#Version
private long modcount;
private String updatedBy;
private DateTime updatedAt;
// getters & setters
}
#Entity
public class SomeEntity {
#Id
private Long id;
#Embedded
private Updateable updateAudit;
// other stuff
}
Is it not possible to have a #Version in an #Embeddable, or is this Hibernate specific?
An embeddable class is just a convinience way of declaring reusable entity elements, i.e. your Updateable could be used in other entities without having to add the fields and the mapping again.
As such, embeddables are part of the entity (as the name suggests they are embedded) and thus independent versioning doesn't make sense.
Adding the #Version annotation to the embeddable only would also not make much sense since the embeddable itself can't be versioned and you'd have to deal with cases where multiple embeddables are contained in a single entity (e.g. which version should be used in that case?). So since #Version only makes sense for entities it's easier to just allow that annotation for entities or mapped superclasses.
Actually although the JPA spec recommends that version properties are numeric, strings or timestamps Hibernate seems to provide user defined version types:
The version column may be a numeric (the recommended solution) or a timestamp. Hibernate supports any kind of type provided that you define and implement the appropriate UserVersionType.
So what you might be able to do (not tested, just derived from the docs) if you want to use Updateable as your version is to provide a custom user type for Updateable and then use it like this:
#Entity
public class SomeEntity {
#Id
private Long id;
#Type( "your.custom.UserVersionType" )
#Version
private Updateable updateAudit;
// other stuff
}
Related
I have a set of Java classes with the following UML diagram:
public class Invoice {
#Id
private long id;
...
}
public class InvoiceDetail {
#Id
private long id;
...
private String productName;
private int quantity;
private double price;
}
My purpose is using JPA annotations to establish the different relationships between them. There is a composition relationship between Invoice and InvoiceDetail, which is resolved using #Embedded and #Embeddable annotations for Invoice and InvoiceDetail respectively. However, a problem appears by establishing the relationships between InvoiceDetail, Class3 and Class4. In these relationships InvoiceDetail must be annotated as #Entity. However, when a class is annotated at the same time as #Entity and #Embeddable, the corresponding server will throw a runtime error during the deployment.
Basing on the information of this website, I have written the following possible solution:
#Entity
public class Invoice {
#Id
private long id;
...
#ElementCollection
#CollectionTable(name="INVOICEDETAIL", joinColumns=#JoinColumn(name="INVOICE_ID"))
private List<InvoiceDetail> invoiceDetails;
...
}
Would be this right in order to resolve my problem?
Thanks in advance.
Although without knowing what the classes really are it is hard to tell, I suppose that you have a design problem. The composition between Class1 and Class2 says that any Class2 instance only exists within the lifecycle of a corresponding Class1 instance. But on the other hand you have Class3 instances and Class4 instances which can / must have a relationship to a Class2 instance.
What I'm trying to say is that from my point of view the relationship between Class1 and Class2 should be a simple association and not a composition. Following this path Class2 would be an Entity in JPA and then you should have your problem solved.
I usually use #Embeddable for classes whose instances never exist by themselfes and #Entity for any class whose instances can exist without other instances. An address for example could be implemented either way but not on the same system. Address would be #Embeddable if I don't want to link addresses but it had to be #Entity if I want to make sure the same address isn't saved in more than one row.
[edit: added after classes 1 and 2 were renamed to Invoice and InvoiceDetails]
Having a composition between Invoice and InvoiceDetails makes perfect sense. But I still think you should avoid the need of double personality for InvoiceDetails. I can think of two solutions (both refactorings):
If you prefer having InvoiceDetails as #Embeddable you could change the associations of Class3 and Class4 to Invoice instead of InvoiceDetails. InvoiceDetails would still be traversable via the Invoice object.
If you prefer keeping the associations as is you could declare InvoiceDetails to be an entity. You could still achieve your composition with a cascading delete (see javax.persistence.CascadeType). As it seems that InvoiceDetails already has it's own table, this probably is the better option.
I checked my JPA applications and haven't found any occurence of the same class being #Entity and #Embeddable. Honestly, I doubt if this is possible at all because the official javadoc of #Embeddable says:
Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.
As #Entity has it's own identity, you would try to declare the same object having two identities - and this can't work.
[/edit]
[edit2: adding code for solution proposal #2]
This code should work with some assumptions (see below). This is the implementation of bi-directional navigation for a 1:n-relationship.
#Entity
public class Invoice {
#Id
private long id;
#OneToMany(mappedBy="invoice", cascade = CascadeType.ALL)
private List<InvoiceDetail> details;
}
#Entity
public class InvoiceDetails {
#Id
private long id;
#ManyToOne
#JoinColumn(name="invoice_id")
private Invoice invoice;
}
Assumptions: Tables are named like the entities, the foreign key column for invoice_details table is named "invoice_id" and both tables have a primary key column named "id". Note that the mappedBy-value "invoice" refers to the entity field while the name-value "invoice_id" refers to the database table.
Be cautious when deleting an Invoice object whose InvoiceDetails still are referenced by your Class3 or Class4 instances - you have to release these references first.
For information about JPA refer to these resources:
The Java EE 7 Tutorial: Persistence
Wikibooks: Java Persistence
Javadoc of Package javax.persistence
[/edit]
Having a embeddable class MultiLanguageText:
#Embeddable
public class MultiLanguageText {
#Field
private String textDe;
#Field
private String textFr;
#Field
private String textIt;
//...
}
And another class that uses this class twice:
#Entity(name = "T_AnotherClass")
public class AnotherClass{
#Id
#GeneratedValue
private long key;
#Embedded
private MultiLanguageText name;
#Embedded
private MultiLanguageText description;
//...
}
The fields got translated perfectly fine into "name_textDe", "description_textDe", "name_textFr" and so on with the spring version 1.2.7.RELEASE.
However to store LocalDate's I wanted to upgrade to Hibernate 5. I followed the process described here: https://github.com/spring-projects/spring-boot/issues/2763#issuecomment-154419889
The process worked fine but the translation of the embedded fields stoped working *. I tried different implicit_naming_strategy and physical_naming_strategy, but non of them worked.
If I annotate the fields as follows it does work, but the process is somewhat cumbersome:
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "textDe", column = #Column(name = "name_textDe", length = MultiLanguageText.MAX_TEXT_LENGTH)),
#AttributeOverride(name = "textFr", column = #Column(name = "name_textFr", length = MultiLanguageText.MAX_TEXT_LENGTH)),
#AttributeOverride(name = "textIt", column = #Column(name = "name_textIt", length = MultiLanguageText.MAX_TEXT_LENGTH)),
})
private MultiLanguageText name;
*Not working does mean I get an exception something along the lines of (as now the field gets mapped without the fieldname prefix and therefore the field exist twice):
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: AnotherClass column: textDe (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:764)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:782)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:778)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:804)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:539)
at org.hibernate.mapping.RootClass.validate(RootClass.java:265)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:443)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
... 48 more
To fix this in Spring Boot 1.4, add this to your application.yaml:
spring.jpa.hibernate.naming.implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl
From the javadocs:
An ImplicitNamingStrategy implementation which uses full composite
paths extracted from AttributePath, as opposed to just the terminal
property part. Mainly a port of the older
DefaultComponentSafeNamingStrategy class implementing the no longer
supported NamingStrategy contract.
Hibernate official documentation says:
If you want to have the same embeddable object type twice in the same
entity, the column name defaulting will not work as several embedded
objects would share the same set of columns. In plain JPA, you need to
override at least one set of columns. Hibernate, however, allows you
to enhance the default naming mechanism through the NamingStrategy
interface. You can write a strategy that prevent name clashing in such
a situation. DefaultComponentSafeNamingStrategy is an example of this.
Consequently, if you wish to have full JPA compliance, stick with your current implementation.
I'm developing an Java-application which stores its data via Hibernate in a database.
One feature of this application is to define templates like types, etc. for reuse. For instance the type has attributes and you can create instances of an type, which has values for the attributes.
The problem is, that I don't know how to ensure that only values for attributes can assigned which the type defines. In my solution there is a redundancy which cause the problem, but I don't know how to remove it.
My current (and problematic) approach looks like this:
#Entity
class Type
{
#Id
#Generated
private Long id;
#OneToMany(mappedBy="type")
private List<Attribute> attributes;
//...
}
#Entity
class Attribute
{
#Id
#Generated
private Long id;
#ManyToOne
private Type type;
//...
}
#Entity
class Instance
{
#Id
#Generated
private Long id;
#ManyToOne
private Type type;
//...
}
#Entity
class AttributeValue
{
#Id
#Embedded
private ResourceAttributValueId id;
#Column(name="val")
private String value;
//...
}
#Embeddable
public class ResourceAttributValueId implements Serializable
{
#ManyToOne
private ResourceStateImpl resource;
#ManyToOne
private ResourceAttributeImpl attribute;
//...
}
There the definition of the type is redundant: Type can be reached via AttributeValue->Attribute->Type and AttributeValue->Instance->Type
Another idea was to use type + attribute name as id of the attribute and instance + attribute name as id of the attribute value, but that doesn't solves my problem.
The key for correctly modeling "diamond-shaped" dependencies like this is the usage of identifying relationships:
(I took a liberty of renaming your entities slightly, to what I believe is a more consistent naming scheme.)
Note how we migrate the TYPE_ID from the top of the diamond, down both sides, all the way to the bottom and then merge it there. So, since there is only one ATTRIBUTE_INSTANCE.TYPE_ID field and is involved in both FKs, we can never have an attribute instance whose attribute type's type differs from instance's type.
While this avoids "mismatched" attributes, it still doesn't ensure the presence of attribute instances (if you support the concept of "required attribute"), which is best enforced at the application level. Theoretically you could enforce it at the database level, using circular deferred FKs, but not all DBMSes support that, and I doubt it would play nicely with ORMs.
Unfortunately, I'm not experienced enough with Hibernate to answer whether this can be mapped there and how.
See also:
Choosing from multiple candidate keys
How to keep foreign key relations consistent in a “diamond-shaped” system of relationships
I wanted to know if there is a way to get in a One2Many relationship a field of the One side that is an aggregate of the Many side.
Let's take the following example:
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
// Here I don't know how to Map the latest B by date
private B latestB;
// Acceptable would be to have : private Date latestBDate;
}
#Entity
public class B {
#Id
private Long id;
private Date date;
#ManyToOne (targetEntity=A.class)
private A parentA;
}
My question is how can I make the mapping of the field latestB in the A entity object without doing any de-normalization (not keeping in sync the field with triggers/listeners)?
Perhaps this question gives some answers, but really I don't understand how it can work since I still want to be able to fetch all childs objects.
Thanks for reading/helping.
PS: I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
PS2: Or just tell me that I should not do this (with arguments of course) ;-)
I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
Implementing the acceptable solution (i.e. fetching a Date for the latest B) would be possible using a #Formula.
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
#Formula("(select max(b.some_date) from B b where b.a_id = id)")
private Date latestBDate;
}
References
Hibernate Annotations Reference Guide
2.4.3.1. Formula
Resources
Hibernate Derived Properties - Performance and Portability
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Filtering.2C_Complex_Joins
Basically JPA does not support this, but some JPA providers do.
You could also,
- Make the variable transient and lazy initialize it from the OneToMany, or just provide a get method that searches the OneToMany.
- Define another foreign key to the latest.
- Remove the relationship and just query for the latest.
I'm currently using Eclipselink, but I know now days most JPA implementations have been pretty standardized. Is there a native way to map a JPA entity to a view? I am not looking to insert/update, but the question is really how to handle the #Id annotation. Every entity in the JPA world must have an ID field, but many of the views I have created do not conform to this. Is there native support for this in the JPA or do I need to use hacks to get it to work? I've searched a lot and found very little information about doing this.
While using the #Id annotation with fields of directly supported types is not the only way to specify an entity's identity (see #IdClass with multiple #Id annotations or #EmbeddedId with #Embedded), the JPA specification requires a primary key for each entity.
That said, you don't need entities to use JPA with database views. As mapping to a view is no different from mapping to a table from an SQL perspective, you could still use native queries (createNativeQuery on EntityManager) to retrieve scalar values instead.
I've been looking into this myself, and I've found a hack that I'm not 100% certain works but that looks promising.
In my case, I have a FK column in the view that can effectively function as a PK -- any given instance of that foreign object can only occur once in the view. I defined two objects off of that one field: one is designated the ID and represents the raw value of the field, and the other is designated read-only and represents the object being referred to.
#Id
#Column(name = "foreignid", unique = true, nullable = false)
public Long getForeignId() {
...
#OneToOne
#JoinColumn(name = "foreignid", insertable=false, updatable=false)
public ForeignObject getForeignObject() {
...
Like I said, I'm not 100% sure on this one (and I'll just delete this answer if it turns out not to work), but it got my code past a particular crash point.
Dunno if it applies to your specific situation, though. And there's an excellent chance that after 11 months, you no longer care. :-) What the hell, that "Necromancer" badge doesn't just earn itself....
In my view I have a "unique" id, so I mapped it as the Entity id.
It works very well:
#Entity
#Table(name="table")
#NamedQuery(name="Table.findAll", query="SELECT n FROM Table n")
public class Table implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="column_a")
private int columnA;
JPA - 2.5.4
CREATE MATERIALIZED VIEW IF NOT EXISTS needed_article as select product_id, count(product_id) as count from product_article group by product_id;
CREATE MATERIALIZED VIEW IF NOT EXISTS available_article as select product_id, count(product_id) as count from article a inner join product_article p
on a.id = p.article_id and a.stock >= p.amount_of group by product_id;
CREATE UNIQUE INDEX productId_available_article ON available_article (product_Id);
CREATE UNIQUE INDEX productId_needed_article ON needed_article (product_Id);
Entity.java
#Entity
#Immutable // hibernate import
#Getter
#Setter
public class NeededArticle {
#Id
Integer productId;
Integer count;
}
Repository.java
#Repository
public interface AvailableProductRepository extends CrudRepository<AvailableArticle, Integer> {
#Query("select available.productId from AvailableArticle available, NeededArticle needed where available.productId = needed.productId and available.count = needed.count")
List<Integer> availableProduct();