JPA: #OrderColumn and visible state of an entity? - java

This is a followup question to:
Is #ManyToMany(mappedBy = ... ) + #OrderColumn supported by the JPA?
I'm referring to the #OrderColumn Java docs:
http://docs.oracle.com/javaee/6/api/javax/persistence/OrderColumn.html
The text there is the same as what the JPA 2 spec writes in section 11.1.39 OrderColumn Annotation.
What does the part "the order column is not visible as part of the state of the entity" mean exactly? There's a lot of room for interpretation on that.
Does that mean the order column must not be part of any FKs and/or PKs defined? Or only not in FKs (PK allowed)? What does the state of an entity comprise? AFAIK the JPA spec doesn't define that.
Thanks

The order column is not a field in the Entity class(es), so it isn't visible (as such).

OPENJPA. Please look at this code, it is the best way to understand
//This is in the parent table
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parentTable", fetch = FetchType.EAGER)
#OrderColumn
private ArrayList<child_class_type> childTable = new ArrayList<child_class_type>();
//This is in the child table
#ManyToOne(fetch = FetchType.EAGER, optional = false)
private parentTableClass parentTable;
This will get an ordered list(child table). :)
John V, Col

Related

JPA join to inherited class

In jpa I have some entity (Content and Product for now but more can come) that should join to Comment entity.
Problem is I don't want to have extra field(and column in table) in Comment entity for each join entity (Products, Contents..) because these entities will increase in future.
I find one semi-solution is to use single table inheritance and create concrete Comment class like CommentContent, CommentProduct, and use discriminator column but joining to entities (Content and Product) still remain.
what do you suggest?
Edit:
sample relation for example between Comment and Content will be #MayToOne that many Comments belongs to one Content and so for Product..
Edit 2
in pure table relationship schema (without ORM like hibernate/jpa) I can and I do this kind of solution:
add to column in comment table 1-item_type and 2-item_id witch item_type specify other side table name (product, content in my question) and item_id specify foreign key to table that it name is at item_type column
How can I model this in Jpa/hibernate ORM?
You can model what you described with Hibernate like
class Content {
#OneToMany
#JoinColum(name = "item_id")
#Where("item_type = 'CONTENT'")
Set<Comment> comments;
}
class Product {
#OneToMany
#JoinColum(name = "item_id")
#Where("item_type = 'PRODUCT'")
Set<Comment> comments;
}
class Comment {
#Id
#GeneratedValue
Long id;
#Enumerated(STRING)
ItemType itemType;
Lont itemId;
}
enum ItemType {
CONTENT,
PRODUCT
}
You can try #ManyToOne for your upcoming entities. So here you don't need to add new columns in Comment entity(base table) Check this example : https://howtoprogramwithjava.com/hibernate-manytoone-unidirectional-tutorial/
What type or relation is this?
If it is #OneToOne you can put information about relation with Comment in Product and Content, and in Comment use option MappedBy to map them, you will skip the extra columns in Comment.
If it is #OneToMany you won't make any column in Comment but rather again put information in Product and Content and only use mappedBy on collection of this objects on the Comment site.
CheckForMoreInfo

JPA Inverse Join with OneToMany causing More than one row with the given identifier

I have a convenient relation set up in which an entity has a one-to-many relationship with another, and that has a many-to-one with another. So, a LISTING has many LISTING_LINE_ITEMS, and those LISTING_LINE_ITEMS have one SERVICE_PERIOD, but a SERVICE_PERIOD has many LISTING_LINE_ITEMS. I have attempted to describe this relationship using JPA's #JoinTable as follows:
LISTING
#OneToMany
#JoinTable (name = "LISTING_LINE_ITEM", joinColumns = #JoinColumn (name = "listing_id"), inverseJoinColumns = #JoinColumn (name = "service_period_id"))
Set<ServicePeriod> servicePeriods;
LISTING_LINE_ITEM
#ManyToOne (fetch = FetchType.EAGER)
#JoinColumn (name = "listing_id", nullable = false)
Listing listing;
#ManyToOne (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn (name = "service_period_id")
ServicePeriod servicePeriod;
SERVICE_PERIOD
#ManyToOne
#JoinTable (name = "LISTING_LINE_ITEM", joinColumns = #JoinColumn (name = "service_period_id"), inverseJoinColumns = #JoinColumn (name = "listing_id"))
Listing listing;
The obvious goal is to be able to easily obtain a list of ServicePeriods for a Listing or a single Listing for a ServicePeriod. Currently the way this is set up I'm getting an exception:
org.hibernate.HibernateException: More than one row with the given identifier was found: 361951, for class: com.gonfind.entity.ServicePeriod
I believe this is because a listing has ListingLineItems that refer to the same ServicePeriod. I'm sure that there is a way to accomplish what I'm after but I don't know what it is.
You do appear to have some problems there. On the technical / JPA side:
you cannot use LISTING_LINE_ITEM both as a join table and as an entity table. There are several reasons for this, but the main reason is that you will confuse JPA: it will try to use that table in different, incompatible ways for those two purposes.
in JPA, a bidirectional relationship is owned by exactly one side; the other side uses the mappedBy attribute of its relationship annotation to reference the owning side.
But you also have data design problems. Your constraint that line items' service periods be restricted to one of those separately associated with the same listing constitutes either
a functional dependency between non-key fields, if the listing id is not part of the line item key, or otherwise
a functional dependency on a subset of a key.
In the first case, your data fail to be in third normal form; in the second case they fail to be even in second normal form. Your trouble modeling this with JPA arises in part from the low level of normalization.
Normalizing your data properly would make things a lot easier on multiple levels. To do that, you need to remove the direct association between listings and line items, and instead associate them through service periods. You then would have:
Listing <-- one to many --> ServicePeriod <-- one to many --> LineItem
Of course, that would have implications on the structure of your application, but it's likely to be a long-term development and maintenance win, and maybe even a usability win, for the application to be aligned with the natural structure of your data like that. If you wish, you could put methods on your Listing entity to allow ListingLineItems to be managed to some extent as if they belonged directly to Listings, and vise versa.
That data organization would look something like this:
LISTING
#OneToMany(mappedBy = "listing",
fetch = FetchType.EAGER,
cascade = CascadeType.PERSIST)
Set<ServicePeriod> servicePeriods;
SERVICE_PERIOD
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "listing_id")
Listing listing;
#OneToMany(mappedBy = "servicePeriod",
fetch = FetchType.EAGER,
cascade = CascadeType.PERSIST)
Set<ListingLineItem> lineItems;
LISTING_LINE_ITEM
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "service_period_id")
ServicePeriod servicePeriod;
If you cannot restructure your data more or less that way, then you're stuck jerry-rigging something that cannot fully be described to JPA. I'm imagining a separate join table for Listing <-> ServicePeriod, a non-JPA FK constraint to that table from the entity table for line items, and, of course, proper form for the various bidirectional relationships.

#ManyToMany relation not save

I have some entities with#ManyToMany relation:
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "buses_drivers",
joinColumns = #JoinColumn (name = "driver_id_inner", referencedColumnName = "driver_id"),
inverseJoinColumns = #JoinColumn (name = "bus_id_inner", referencedColumnName = "bus_id"))
private List<Bus> buses;
and
#ManyToMany(mappedBy = "buses", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Driver> drivers;
When execute saving Driver model with some Bus models, all ok. Tables buses_drivers store all keys those entities. But when saving Bus model with drivers, table doesn't change. I think problem with inverseJoinColmns mapping.
That is the expected behaviour. In a bidirectional many-to-many association one side has to be the inverse side. In your case it is the Bus side because it contains mappedBy:
The field that owns the relationship. Required unless the relationship
is unidirectional.
That means that Driver is the owner of the association and Hibernate will only check that side when maintaining the association.
You should definitely redesign your relations.
Without even getting into the problems with your current save scenario, with bidirectional #ManyToMany + CascadeType.ALL, you're destined to get even more troubles.
For example, deleting one bus will due to cascade, delete all its drivers, which due to cascade again, will delete all its buses. You'll basically end up deleting much more than you probably want. Also, check the SQL generated by these mappings, you'll most likely notice that its far from ideal.
For people doesn't understand from the accepted answer. This is more appropriate : Java: saving entities with ManyToMany association
I came across with this problem in test cases when filling test data.
When there is an owning side you just can save child just with owner.

JPA: implicit cascades for relationships mapped as #ManyToMany #JoinTable?

I have the following mapping:
#Entity
#Table(name = "Prequalifications")
public class Prequalification implements Serializable
{
...
#ManyToMany
#JoinTable(name = "Partnerships", joinColumns = #JoinColumn(name = "prequalification_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "company_id", referencedColumnName = "id"))
private Set<Company> companies;
...
}
In a #ManyToMany + #JoinTable mapped relationship, isn't it kind of implicit that the association (link) entities (here Partnerships) are automatically persisted, removed, etc. even though
by default, relationships have an empty cascade set
? The above quote was taken from "Pro JPA 2, by Mike Keith".
Executing
em.merge(prequalification);
on the above entity does persist the associated partnerships without any cascade types specified.
Am I correct that this implicit cascade has to be performed? This isn't mentioned anywhere I looked...
The rows in the join table will be inserted/deleted as part of the owning Entity (if bi-directional the side without the mappedBy). So if you persist or remove or update the Prequalification the join table rows will also be inserted or deleted.
The target Company objects will not be cascaded to. So on remove() they will not be deleted, if the list is updated they will not be deleted unless orphanRemovla is set. Persist should also not be cascaded, but what happens when you have references to "detached" objects is somewhat of a grey area. Technically an error should be thrown, because the object is new and the relationship was not cascade persist. It may also try to insert and get a constraint error. It should not cascade the persist, although your object model is technically in an invalid state, so what occurs may depend on the provider.
Wanted to add a comment, but don't have enough rep for it.
I had the same question as #D-Dá´™um: "Where in the docs can we find a reference to this behaviour?"
I found it in the Hibernate docs (many-to-many).
If you scroll just a bit just below the code example there, you will find:
When an entity is removed from the #ManyToMany collection, Hibernate simply deletes the joining record in the link table. Unfortunately, this operation requires removing all entries associated with a given parent and recreating the ones that are listed in the current running persistent context.
Where the "link table" refers to the "join table".
Hope this helps.

Attributes on Many-to-Many relationships (Hibernate)

I have entity classes A and C. They are mapping the tables tblA and tblC and have a many-to-many relationship between them, with tblB to map between them. tblB contains A_ID, C_ID and SetDate, the last one being the date it was set, thus an attribute to the relationship. My question is, how do I best map in this attribute? At the moment they're unmapped, like this:
A:
#ManyToMany(targetEntity=C.class, cascade={ CascadeType.PERSIST, CascadeType.MERGE } )
#JoinTable(name="tblB", joinColumns=#JoinColumn(name="A_ID"), inverseJoinColumns=#JoinColumn(name="C_ID") )
private Collection<C> Cs;
C:
#ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "Cs", targetEntity = A.class )
private Collection<A> As;
How should I get tblB.SetDate out of this?
Cheers
Nik
From what I know, it is not possible to map it this way, you have to switch to One-To-Many and a Many-To-One relationships, with your B in the middle. Your date will be an attribute of B.
For this lack of evolutivity, the Hibernate documentation recommends to avoid the Many-To-Many in general, and use the two relationships from the beginning.
See
#ManyToMany Hibernate Question (can add extra field?)
And
how to make a composite primary key (java persistence annotation)
regards,

Categories

Resources