JPA: one-to-one + self referential + bidirectional - java

I have an entity called 'Instructions'. Sometimes each Instructions has to keep track Instructions before and after it. For Example I have new instruction B which is continuing from existing instruction A, Instruction B has to aware that Instruction A is the previous instruction, while Instruction A also has to know that Instruction B is the next after it. Not every Instruction will have before and after Instruction.
How to implement this in JPA(EclipseLink): [one-to-one + self referential + bidirectional] relation?
So far (not working yet) i came up with this:
mysql db:
CREATE TABLE instructions (
instruction_id int(11) NOT NULL AUTO_INCREMENT,
instruction_title varchar(100) NOT NULL,
instruction_text varchar(999) NOT NULL,
instruction_previous_id int(11) DEFAULT NULL,
PRIMARY KEY (instruction_id),
CONSTRAINT instructions_ibfk_3
FOREIGN KEY (instruction_previous_id)
REFERENCES instructions (instruction_id));
entity:
#Entity
#Table(name = "instructions")
public class Instructions implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "instruction_id")
private Integer instructionId;
#Basic(optional = false)
#Column(name = "instruction_title")
private String instructionTitle;
#Basic(optional = false)
#Column(name = "instruction_text")
private String instructionText;
#JoinColumn(name="instruction_previous_id", referencedColumnName = "instruction_id", nullable = true)
#OneToOne(optional = true)
private Instructions instructionPrevious;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "instructionPrevious")
private Collection<Instructions> instructionNextCollection;
// other properties, setter & getter
}
Currently no problem at create new Instruction, had error at reading
Instructions instruction = em.find(Instructions.class, instructionId);
instruction.getInstructionNextCollection().size(); //error this line
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'atiits.instructions_instructions' doesn't exist
Error Code: 1146
Call: SELECT t1.instruction_id, t1.instruction_urgent, t1.instruction_uploaded_by, t1.instruction_translate, t1.instruction_title, t1.instruction_type, t1.instruction_translate_received, t1.instruction_is_cancelled, t1.instruction_translate_sent, t1.instruction_had_workorder, t1.instruction_text, t1.instruction_update_date, t1.instruction_update_by, t1.instruction_create_by, t1.instruction_translator, t1.instruction_create_date, t1.instruction_company_id, t1.instruction_previous_id, t1.instruction_status_id FROM instructions_instructions t0, instructions t1 WHERE ((t0.Instructions_instruction_id = ?) AND (t1.instruction_id = t0.instructionNextCollection_instruction_id))
bind => [874]
Query: ReadAllQuery(name="instructionNextCollection" referenceClass=Instructions sql="SELECT t1.instruction_id, t1.instruction_urgent, t1.instruction_uploaded_by, t1.instruction_translate, t1.instruction_title, t1.instruction_type, t1.instruction_translate_received, t1.instruction_is_cancelled, t1.instruction_translate_sent, t1.instruction_had_workorder, t1.instruction_text, t1.instruction_update_date, t1.instruction_update_by, t1.instruction_create_by, t1.instruction_translator, t1.instruction_create_date, t1.instruction_company_id, t1.instruction_previous_id, t1.instruction_status_id FROM instructions_instructions t0, instructions t1 WHERE ((t0.Instructions_instruction_id = ?) AND (t1.instruction_id = t0.instructionNextCollection_instruction_id))")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:333)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:687)

There's some confusion in your example whether each Instruction can be followed by a single Instruction or by many.
If it's a single, then don't use a collection for instructionNext.
If it's many, then the example code in JPA: How to have one-to-many relation of the same Entity type should help. You need #ManyToOne for the preceding instruction and #OneToMany for the following, rather than #OneToOne.

Related

only select columns of the a psql table could receive data

Something very bizarre have been happening. I have a very simple Entity recipe like so
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class Recipe {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#Column(name = "date_added", nullable = false)
private String dateAdded;
#Column(name = "last_edited", nullable = false)
private String lastEdited;
}
and I have this post service that should post the 4 string attribute to the database
public void postRecipe(Recipe recipe){
var sql = """
INSERT INTO public.recipe ("name","instructions","date_added","last_edited")
VALUES (?, ?, ?, ?)
""";
jdbcTemplate.update(
sql,
recipe.getName(),
recipe.getInstructions(),
recipe.getDateAdded(),
recipe.getLastEdited()
);
}
However when the following jason is sent using postman, I get the null value error.
{
"name":"test",
"instructions":"don't eat",
"date_added":"03/04/2017",
"last_edited":"03/04/2017"
}
ERROR: null value in column \"date_added\" of relation \"recipe\" violates not-null constraint\n Detail: Failing row contains (3, null, don't eat, null, test)
The strangest thing is that only the "name" and "instruction" columns receive their data and not the other columns. I have tried adding another String attribute to the Entity class and it also cannot get data from the jason.
Edit 1:
I have tried adding the data directly through pgadmin and it worked fine
INSERT INTO recipe (name, instructions, date_added, last_edited)
VALUES ('test', 'test instruction', '2020/03/05', '2020/05/08');
It looks like your deserialization is broken - transforming your JSON into the Java entity, which results in some null values present. Most likely because date_added != dateAdded (field name), and Jackson cannot properly set a value.
I recommend having a look at Jackson guide: https://www.baeldung.com/jackson-annotations, #JsonProperty specifically. And overall do not mix entities and DTOs
After many trials and errors I was able to come up with a solution but still have no clue as to why this is happening. It turns out the under score in the annotation is the problem.
//won't work
#Column(name = date_added)
//works
#Column(name = dateadded)
This is pretty strange because I am fairly certain that the under score is generated by hibernate.
if anyone know why this is happening please let me know... for now I will just stay away from the under scrolls.

Primary key not returned in OneToOne join fetch in eclipselink

I have an issue join fetching in case of OneToOne relation in the same class. Example follows:
class Data {
...
#Id
#Column(name = "DATA_ID")
Long id;
#Column(name = "DATA_OWNER_ID")
#ForeignKey(entityClass = Owner.class)
Long ownerId;
#Column(name = "DATA_RELATED_ID")
#ForeignKey(entityClass = Data.class)
Long relatedDataId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DATA_RELATED_ID", referencedColumnName = "DATA_ID", insertable = false, updatable = false)
Data relatedData;
}
I want to select data based on some conditions, while also fetching/initialising the "relatedData", all in one JPQL query:
SELECT owner.something1, data
FROM Data data
JOIN Owner owner on data.ownerId = owner.id
JOIN FETCH data.relatedData
WHERE data.something2 = :expectedSomething2
Executing that JPQL query throws an exception:
Query: ReadObjectQuery(name="relatedData" referenceClass=Data)|Exception:
javax.persistence.PersistenceException: Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [DatabaseRecord(
DATA_X => something
DATA_Y => something2
...
)] during the execution of the query was detected to be null. Primary keys must not contain null.
Which is somewhat true, as there is no DATA_ID column listed. Changing JOIN FETCH to LEFT JOIN FETCH returns both owner.something1 and data, but the relatedData object is null (relatedDataId is not null).
I can see, that the id for relatedData is returned from DB, but eclipselink trims it in valueFromRowInternalWithJoin and trimRowForJoin methods.
The Id column name attribute value is the reason of this exception. Same issue found in eclipselink version 2.3.2 but it works fine in version 2.0.0
Try with this entry :
eclipselink.jpa.uppercase-column-names=true
OR Try with upper and lower case one by one which on will work for you.
#Id
#Column(name = "UUID") // UUID - uppercase/lowercase one by one
Long id;
I've somehow resolved this issue, but haven't had the time to correctly identify the cause. Final (working) version differences are:
I could've forgotten to add get/set for relatedData
I have specified targetEntity = Data.class in #OneToOne
Fetch is now a LEFT JOIN FETCH and appears before JOIN Owner owner

Hibernate - byNaturalId not working

I have a user entity identified by two natural ids, something like
#Entity
#Table(name = "user", uniqueConstraints =
{ #UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "nick") })
public User()
{}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private id;
#Column(name = "email", unique = true, nullable = false, length = 31)
#NaturalId(mutable = true)
private String email;
#Column(name = "nick", unique = true, nullable = false, length = 31)
#NaturalId(mutable = false)
private String nick;
However, when I try to execute
session.byNaturalId(User.class).with(LockOptions.READ).using("email", "admin#mail.com").load();
it throws an exception
org.hibernate.HibernateException: Entity [pervasive.com.gmail.tigerjack89.forum.shared.model.entities.User] defines its natural-id with 2 properties but only 1 were specified
at org.hibernate.event.spi.ResolveNaturalIdEvent.<init>(ResolveNaturalIdEvent.java:75)
at org.hibernate.event.spi.ResolveNaturalIdEvent.<init>(ResolveNaturalIdEvent.java:52)
at org.hibernate.internal.SessionImpl$BaseNaturalIdLoadAccessImpl.resolveNaturalId(SessionImpl.java:2607)
at org.hibernate.internal.SessionImpl$NaturalIdLoadAccessImpl.load(SessionImpl.java:2722)
at pervasive.com.gmail.tigerjack89.forum.server.model.orm.StorageManager.getByNaturalId(StorageManager.java:217)
at pervasive.com.gmail.tigerjack89.test.local.MyHibernateTest.test1(MyHibernateTest.java:37)
at pervasive.com.gmail.tigerjack89.test.local.MyHibernateTest.main(MyHibernateTest.java:23)
Why is this? I think it's also due to the log of the SQL syntax generated by Hibernate. Indeed, it is strange (redundant) at this point and I think it's the cause of the exception
Hibernate:
alter table user
add constraint UK_t8tbwelrnviudxdaggwr1kd9b unique (email, nick)
Hibernate:
alter table user
add constraint UK_ob8kqyqqgmefl0aco34akdtpe unique (email)
Hibernate:
alter table user
add constraint UK_pvnbxcfihb58o5n2n1fnc7fh1 unique (nick)
EDIT: Reading the code again, I thought that the problem could be related to the #UniqueConstraints annotations. However, even if I try to remove one of them, Hibernate continues to genetate the above SQL syntax.
I would make the email address alone the NaturalId and then your query would work.
When two columns are identified as the NaturalId it creates a Composite Key.
Nick could still be used as a foreign Key.
You've a composite naturalId key (email,nick) so you can have multiples results with simple email arg.
you've to use
session
.byNaturalId(User.class)
.with(LockOptions.READ)
.using("email", "admin#mail.com")
.using("nick", "admin")
.load();
You can also use Metamodel
session
.byNaturalId(User.class)
.with(LockOptions.READ)
.using(User_.email.getName(), "admin#mail.com")
.using(User_.nick.getName(), "admin")
.load();

JPA Error : Primary Key was detected to be NULL

I have a problem with JPA querying a MySQL table that has a column of type geometry. It contains polygons having sets of latitude and longitude as the coordinates. While executing the nativequery to select from the table, I am getting the following error
Exception Description: The primary key read from the row [ArrayRecord(
=> POLYGON((102.642944444444 2.9757087270706,102.642944444444 2.79805447470818,....
=> 16.0
=> 325990)] during the execution of the query was detected to be null. Primary keys must not contain null.
However the table has no row with primary key as null. This specific row has a very large polygon with 66 coordinates. Not sure if the problem is because of this.
Following are the table column names and types
geomarea - geometry
riskvalue - double
id - int (Autoincrement, Primary Key)
Following is the code in my EJB to read the table.
Query query = em.createNativeQuery("select astext(geomarea) geomarea,riskvalue,id from earthquakeRisk where Contains(geomarea,GeomFromText('POINT(" + node.getLongitude() + " "+node.getLatitude()+")'))",Earthquakerisk.class);
geomList.addAll(query.getResultList());
And here is how the fields are declared in the entity class
public class Earthquakerisk implements Serializable {
private static final long serialVersionUID = 1L;
#Basic(optional = false)
#NotNull
#Lob
#Column(name = "geomArea")
private byte[] geomArea;
#Column(name = "riskvalue")
private Double riskvalue;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
Any idea how to solve this?
I found a solution to the problem. Adding it here in case someone finds it useful.
Solved it by removing the Earthquakerisk.class from the query, and changing my List to List. So the working code is now as follows.
List<Object[]> geomList = new ArrayList<>();
Query query = em.createNativeQuery("select astext(geomarea) geomarea,riskvalue,id from earthquakeRisk where Contains(geomarea,GeomFromText('POINT(" + node.getLongitude() + " "+node.getLatitude()+")'))");
geomList.addAll(query.getResultList());

Caused by: org.hibernate.MappingException: Repeated column in mapping for entity

I am beginner in handling JPA with maven and JBOSS, with Restful to make my application I have the following problem arose me doing DEPLOY
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: com.company.test_resources_war_1.0-SNAPSHOTPU] Unable to build EntityManagerFactory
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: database.Photo column: fid_module (should be mapped with insert = \ "false \" update = \ "false \") "}}
Not that step, check all posles solutions, but did not find anything, can someone help me??
Thanks in advance
Below I show the SQL code in postgres that I have and I did the mapping.
I have three tables (activity, event and photo) where one of them (photo) refers to the other two (activity and event) but in a single column (photo.fid_module)
SQL Code (enginer database-->Postgresql):
CREATE TABLE activity (
id_activity integer not null,
name character varying(150),
description text,
CONSTRAINT id_activity_pk PRIMARY KEY (id_activity)
)
CREATE TABLE event (
id_event integer not null,
name character varying(150),
description text,
date timestamp without time zone,
CONSTRAINT id_event_pk PRIMARY KEY (id_event)
)
CREATE TABLE photo(
id_photo integer not null,
path character varying(150),
fid_module integer not null,
CONSTRAINT id_photo_pk PRIMARY KEY (id_photo),
CONSTRAINT fk_photo_activity FOREIGN KEY (fid_module)
REFERENCE activity (id_activity) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_photo_event FOREIGN KEY (fid_module)
REFERENCE event (id_event) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Now the mapping I did with the help of Netbenas and gave me the following code (I did the mapping for the three tables, but in presenting me the problem is in the class Photo.java).
#Entity
#Table(name = "photo")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "photo.findAll", query = "SELECT p FROM Photo p"),
#NamedQuery(name = "photo.findByFidPhoto", query = "SELECT p FROM Photo p WHERE p.fidphoto = :fidphoto"),
#NamedQuery(name = "photo.findByIdPhoto", query = "SELECT p FROM Photo p WHERE p.idphoto = :idphoto")})
public class Photo implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_photo")
private Integer idPhoto;
#Column(name = "path")
private Recurso fidPath;
#JoinColumn(name = "fid_module", referencedColumnName = "id_activity")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private SliderWebHome fidModule;
#JoinColumn(name = "fid_module", referencedColumnName = "id_event")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Publicacion fidModule1;
public ModuloRecurso() {
}
.......
}
I am using JPA for persistence (but do mvn clean install and mvn jboss-as: deploy several pulls me hibernate dependencies) could anyone tell me what is my mistake or could solve this problem. Thank you.
You have two column mapped with the same name
#JoinColumn(name = "fid_module", referencedColumnName = "id_activity")
#JoinColumn(name = "fid_module", referencedColumnName = "id_event")
Change one of the name attribute!
Looking in your exception, you can read:
Repeated column in mapping for entity
As noted in another answer, your Java code specifies the same join-column name for two fields, which can't work.
If this Java code is generated by a netbeans mapping tool, as it seems from your note
Now the mapping I did with the help of Netbenas and gave me the following code ...
the bad Java mapping is probably caused by a bad combination of constraints in your SQL.
You have in your definition of the photo table:
CONSTRAINT fk_photo_activity FOREIGN KEY (fid_module)
REFERENCE activity (id_activity) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_photo_event FOREIGN KEY (fid_module)
REFERENCE event (id_event) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
which attempts to make the column fid_module a foreign key referencing activity and also a foreign key referencing event, which can't work.
If you need foreign keys from photo to both of those tables, you'll need to use two different columns.

Categories

Resources