Netbeans - Glassfish server deployment error due to composite primary key - java

I'm currently having an issue with a BUILD FAILED error when deploying my Netbeans project to Glassfish server. I get the following error from the log:
Exception Description: The #JoinColumns on the annotated element [field instrument] from the entity class [class entity.InstrumentExtRef] is incomplete. When the source entity class uses a composite primary key, a #JoinColumn must be specified for each join column using the #JoinColumns. Both the name and the referencedColumnName elements must be specified in each such #JoinColumn.. Please see server.log for more details.
The table that's having the issue is instrument_ext_ref and has a composite primary key of the following fields: instrument_id and instrument_code_type.
The entity class that seems to have the problem - InstrumentExtRef - has the following declarations and annotations.
#EmbeddedId
protected InstrumentExtRefPK instrumentExtRefPK;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 16)
#Column(name = "reference")
private String reference;
#JoinColumn(name = "instrument_code_type", referencedColumnName = "code_type", insertable = false, updatable = false)
#ManyToOne(optional = false)
private InstrumentCodeType instrumentCodeType1;
#JoinColumn(name = "instrument_id", referencedColumnName = "id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Instrument instrument;
And the entity class containing the #EmbeddedId details (InstrumentExtRefPK) looks like this:
#Embeddable
public class InstrumentExtRefPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "instrument_id")
private int instrumentId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#Column(name = "instrument_code_type")
private String instrumentCodeType;
Class InstrumentCodeType refers to table instrument_code_type which has a primary key code_type (the referenced column). Class Instrument refers to table instrument which itself has a composite primary key consisting of columns "id" (the referenced column) and column "exchange_exchange_code".
It seems obvious from the error message that the composite primary key in the InstrumentExtRef entity is the issue but based on my research of other similar (but not quite the same) issues I think my entity classes look OK. But I'm relatively new to this and may be missing something.

After doing some more research on creating/uses of composite keys in entity classes I saw a subtle point in the error message I was receiving.
"...must be specified for each join column using the #JoinColumn*s*". I wasn't using the #JoinColumns annotation. Or at least Netbeans didn't create the entity class that way. Probably because each join column is from a different source entity/table.
After correcting my entity classes as follows the project was able to be deployed successfully.
#JoinColumns({
#JoinColumn(name = "instrument_code_type", referencedColumnName = "code_type", insertable = false, updatable = false),
#JoinColumn(name = "instrument_id", referencedColumnName = "id", insertable = false, updatable = false)})
private InstrumentCodeType instrumentCodeType1;
private Instrument instrument;
It was here that I started to see what the problem might be:
https://access.redhat.com/site/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Hibernate_Annotations_Reference_Guide/ch02s02s06.html
I suspect that my next problem will be something to do with the fact that the composite primary key is made up of columns from two different entities - instrument and instrumentCodeType. But for now the above change got me past the build problem and if that next problem arises I'll deal with it separately.

Related

Spring JPA - referencedColumnNames [...] not mapped to a single property - Issue with #IdClass

I am facing the issue reported as subject when dealing with the following example:
[other irrelevant annotations]
#IdClass(KeyDAO.class)
public class KeyDAO implements Serializable {
#Id
#NotNull
private String KEYGROUP;
#Id
#NotNull
private String KEYVAL;
[...]
#OneToMany(mappedBy = "KEY")
#ToString.Exclude
private List<OrderDateDAO> ODATES;
}
[other irrelevant annotations]
#IdClass(OrderDateDAO.class)
public class OrderDateDAO implements Serializable {
[...]
#Id
#NotNull
private String ODTTYPE;
[...]
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = Constants.DART_KEYGROUP_ODTTYPE, referencedColumnName = "KEYGROUP")),
#JoinColumnOrFormula(column = #JoinColumn(name = "ODTTYPE", referencedColumnName = "KEYVAL", insertable = false, updatable = false))
})
#ToString.Exclude
#OrderBy("ORDERASC ASC")
private KeyDAO KEY;
}
Basically, OrderDateDAO shall be joined with KeyDAO, and both use an #IdClass annotation. By following the tips on this site, I decided to use a JoinColumnsOrFormulas annotation, because:
the real column to join is ODTTYPE <-> KEYVAL
the KEY table shall be narrowed down by setting a specific fixed value for the KEYGROUP column
Currently, I am getting the following exception:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(KEYGROUP, KEYVAL) of eu.unicredit.dtm.dtm_be.dao.oracle.OrderDateDAO.id.KEY referencing eu.unicredit.dtm.dtm_be.dao.oracle.KeyDAO not mapped to a single property
I have tried several other options, but still the error occurs.
Any help or suggestions here?
Thank you.
Regards,
A.M.

Why MERGE operation works when saving parent with no primary key which has children with primary key?

I have following classes in bidirectional many to many relationship.
#Table(name = "message")
#Entity
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "message_id", unique = true, nullable = false)
private int id;
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.MERGE)
#JoinTable(name = "tags_messages",
joinColumns = #JoinColumn(name = "message_id", referencedColumnName = "message_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "tag_id"))
private Set<Tag> tags=new HashSet<>();
and
#Table
#Entity(name = "tag")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tag_id", unique = true, nullable = false)
private int id;
#Column(name = "name", unique = false, nullable = false)
private String name;
#JsonIgnore
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.MERGE)
private Set<Message> messages;
When trying to save new Message, I got exception saying: "detached entity to persist...Tag". I got it to work by setting CascadeType.MERGE, but I don't understand why it is working. It would be great if someone can explain me why :)
Steps I did which lead to exception:
In db I already had two Tags objects and no Messages. I also had connecting empty table messages_tags
On frontend (Android) I create new Message object (without id), add one Tag (entire object, pulled from db, with id) to Message.
Send new Message to backend (using Retrofit). Hit my controller function, then service function in which I tried to save new Message with accompanying child Tags. At first, my cascading type annotation on both side, was like this:
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
I thought, since I have one part of relationship covered, that I need to cover other one as well. So I did this:
newMessage.getTags().forEach(t -> t.getMessages().add(newMessage));
messageRepository.save(newMessage) //Bum! exception
I commented out that line for setting other part of relationship, set MERGE as cascading type and save simply worked. WHY? Are there any other consequences I may experience while doing other CRUD operations on any of these entities?
When you add a new Tag to the Message on the frontend, you have a different persistent context from the one used on backend. That's why the tag entity is seen as detached (it has a PK but it is not in the backend's persistent context). Since you did not specify a cascade type for JPA to know that to do with Tag instance, the persist of the Message instance fails.
Once you specify the cascade type MERGE, the Tag instance is merged into the backend's persistent context and the save succeeds.
You can avoid the using of MERGE cascade by saving first the Tag instance.

Spring Hibernate MySQL Unidirectional ManyToOne

I have two (very simple) entities: Parent and Child.
#Entity
class Child {
#Id
#Column(name = "id", nullable = false, updatable = false, columnDefinition = "BINARY(16)")
private UUID id;
#Column(columnDefinition="varchar(4000)")
private String obs;
#NotNull
#ManyToOne(optional = false)
#JoinColumn(nullable = false, updatable = false)
private Parent parent;
(getters and setters)
}
The original obs field had no annotation, so Hibernate created it as a varchar(255), but I needed it to be able to handle a larger text. After the changes (as shown above), the method getParent() always returns null.
Does anyone have any idea why this is happening?
For your Column annotation, rather than using columnDefinition set the length element. Here is the Javadoc of length:
(Optional) The column length. (Applies only if a string-valued column is used.)
Default:
255
You can also see why it defaulted to VARCHAR(255) in your schema: the default is 255. Why your columnDefinition doesn't work, however, I don't know (I've no experience with it).

how to address a database column multiple times using hibernate

I have a project to access a database via REST, in which an old database has to be addressed in a modern way using javax.persistence annotations and JPA.
One of my classes fields look like this:
#Column(name = "properties_id", nullable = false)
private int propertiesId;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "properties_id", referencedColumnName = "dbid", insertable = false, updatable = false)
private PropertyList propertyList;
#Id
#Column(name = "propertiesIdx", nullable = false)
private int propertiesIdx;
As you can see in the fields, the column properties_id is used as part of the primary key and as a foreign key. Unfortunately, there is no possibility to change the database layout.
In the application, I need both fields: one for joining the parent-table and another for addressing the property directly.
But in the way it is currently implemented, it doesn't work.
Does somebody have an idea how to address this problem?
For further details of the problem please feel free to visit the git-repo:
https://github.com/SerNet/verinice-rest-service/tree/import

EclipseLink fails to create complete JoinTable for ManyToMany

I'm currently using EclipseLink 2.5 for a project and encountered a little problem with a ManyToMany Relation:
I have a mapped superclass Identifiable that defines a composite key (uuid, revision). Both Entity classes Appointment and Content are subclasses of Identifiable.
Now I try to define an unidirectional ManyToMany relation from Appointment to Content, but it seems that EclipseLink doesn't create the JoinTable right. There are only two columns in it (uuid, revision), but there should be four (one uuid and revision for each side of the relation).
Lust but not least, the code:
#MappedSuperclass
#IdClass(IdentifiablePK.class)
public abstract class Identifiable implements Serializable {
#Id
#UuidGenerator(name = "uuid")
#GeneratedValue(generator = "uuid")
protected String uuid;
#Id
protected Integer revision;
/* ... */
}
public class Appointment extends Identifiable {
#JoinColumns({
#JoinColumn(columnDefinition = "trainingcontent_uuid", referencedColumnName = "uuid", nullable = false, updatable = false, insertable = false),
#JoinColumn(columnDefinition = "trainingcontent_revision", referencedColumnName = "revision", nullable = false, updatable = false, insertable = false)
})
#ManyToMany(fetch = FetchType.LAZY)
protected List<TrainingContent> trainingContents;
/* ... */
}
public class TrainingContent extends Identifiable {
/* ... */
}
I also tried to use #JoinTable instead of #JoinColumns, but then EclipseLink complains about the missing resepectively incomplete #JoinColumns Annotation, which is necessary for target entities with composite keys.
Am I doing something wrong?
Greetings,
chrert
For ManyToMany you must use a #JoinTable to #JoinColumns, the source and target join columns are specified inside the #JoinTable.
Your #JoinColumn must not have, "updatable = false, insertable = false", this makes no sense, as then it would not be able to insert anything, which appears to be what is happening.
See,
https://en.wikibooks.org/wiki/Java_Persistence/ManyToMany

Categories