Unidirectional #OneToMany creates unique index at jon table which is not expected - spring-data-jpa

I'm puzzled with JPA behaviour. Underlying DB is H2, I'm using SpringBoot and
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect
Here is my entity:
#Entity
class ChildEntity{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Long = _
#OneToMany(fetch = FetchType.EAGER)
var parentEntities: java.util.Set[ParentEntity] = _
}
Here is what's created in DB:
CREATE INDEX "PUBLIC"."INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID");
CREATE PRIMARY KEY "PUBLIC"."PRIMARY_KEY_CA" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID", "PARENT_ENTITIES_ID");
-- why?
CREATE UNIQUE INDEX "PUBLIC"."UK_INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("PARENT_ENTITIES_ID");
Why does it create unique index for PARENT_ENTITIES_ID on join table?

Jpa create join table automatically.
You can avoid extra join table issue using #JoinColumn .
The #JoinColumn annotation helps Hibernate to figure out that there is a Foreign Key column in parent.

This is to be expected, a one to many relationship connects two different entities together. JPA by default implements this when you don't do it explicitly. That is why you have a join table with the id for both entities.

Related

JPA OneToMany column name

How to set the column name of the foreign key when setting up a one-to-many relationship in JPA?
I would like to change the name of "items_id" to "item_id"
#OneToMany
private List<Item> items;
I tried the following annotations with no success:
#JoinColumn(name="item_id") // join table is not created
#Column(name="item_id") // no effect
You want to override the mappings of the default values of the join table, so the #JoinTable annotation is the one to use. You want to override the name of the inverseJoinColumn from items_id to item_id:
#OneToMany
#JoinTable(inverseJoinColumns=#JoinColumn(name="item_id"))
List<Item> items;
#OneToMany and #JoinColumn makes a different mapping, a join table is not created and a foreign key is created in the referenced entity table(in the Item table in your case).
#Column is used to override the names for entity attributes, not for relationships.

Does #OneToOne imply uniqueness?

I annotated my fields with only #OneToOne and when I check the database (generated using liquibase) saw that there are unique constraints on database columns.
Does this mean #OneToOne implies uniqueness just by itself, eg. one Building can only be in one city, and no other Buildings can be in the same city?
What do I do when I want to tell that there may be other other buildings in the same city?
add #JoinColumn(unique = false),
only use #JoinColumn(unique = false) without #oneToOne,
or use #ManyToOne?
or leave it without any annotations?
I don't want to put a Buildings field in the city class, because I wouldn't ever call city.getBuildings();. Does any of the below require a bidirectional reference?
class Building {
#OneToOne(optional = false)
City city;
}
class Building {
#OneToOne(optional = false)
#JoinColumn(unique = false)
City city;
}
class Building {
#JoinColumn(unique = true)
City city;
}
class Building {
#ManyToOne
City city;
}
The JPA specification says that for a bidirectional OneToOne relationship (2.10.1 Bidirectional OneToOne Relationships):
Assuming that:
Entity A references a single instance of Entity B.
Entity B references a single instance of Entity A.
Entity A is specified as the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
In case of unidirectional OneToOne relationship (2.10.3.1 Unidirectional OneToOne Relationships):
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
If you have a City-Building relationship, then for any reasonable city it would be a OneToMany/ManyToOne relationship, since a given city can have multiple buildings, but a given building can be only in one city.

Why won't my EJB Entity work with a non-relational table, when using createNativeQuery?

I'm trying to use entities with a MySQL ndbcluster table. This table type doesn't allow foreign keys, but up until now it hasn't been a problem with my entities.
However, I have run into a bit of a problem, when I try to load an entity using the EntityManager's createNativeQuery method. I need to use this method due to my inability to do this: How to make a CriteriaBuilder join with a custom "on" condition?
My MySQL table looks like this:
CREATE TABLE `category` (
`id` SMALLINT(6) NOT NULL AUTO_INCREMENT,
`category_id` SMALLINT(6) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `category_id` (`category_id`)
)
COLLATE='utf8_general_ci'
ENGINE=ndbcluster
ROW_FORMAT=DEFAULT
If I change the table engine to innodb, and add foreign keys, the createNativeQuery method works fine.
My entity class looks like this:
#Entity
#Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Short id;
#OneToMany(mappedBy = "categoryId")
private List<Category> categoryList;
#JoinColumn(name = "category_id", referencedColumnName = "id")
#ManyToOne
private Category categoryId;
public Category() {
}
// getters and setters
}
Even without foreign keys, this entity works fine when I use the CriteriaBuilder for a query, but unfortunately not everything is possible with the CriteriaBuilder.
I get an error when I call getResultList on a Query object created with createNativeQuery. I don't know if this is a bug, or if something should be added to my entity class to make this work.
The error says:
Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
=> 2519
=> 2463
=> Tools)] during the execution of the query was detected to be null. Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Category sql="select * from `category`")
at org.eclipse.persistence.exceptions.QueryException.nullPrimaryKeyInBuildingObject(QueryException.java:895)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:584)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:560)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:717)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:769)
...
My table contains 1 row, where id=1 and category_id=null, so there are no primary keys with a null-value, despite what the error says. If I remove that row or set category_id=1, I don't get an error.
Need help, please.
Managed to make it work by switching from EclipseLink (JPA 2.0) to OpenJPA (JPA 2.0). Seems like there is a bug somewhere in EclipseLink 2.3.2 and/or GlassFish 3.1.2.2.
I've used EclipseLink (JPA 2.0) in another project of mine, using a slightly different version Netbeans + GlassFish 3.1.1, where I used createNativeQuery on an entity class for a non-relational myisam table. This never caused any problem. It really must be a bug.
But problem solved. Bye, bye EclipseLink, hello OpenJPA.
The issue is case sensitivity. In MySQL your column "id" will be defined in the database as "ID" unless you quote it. If you switch your mappings to upper case it should fix the issue (i.e. "ID").
You could also quote the column name ("'id'")
or set the persistence unit property,
"eclipselink.jpa.uppercase-column-names"="true"

jpa OneToMany & ManyToOne

i have a oneToMany and ManyToOne mapping in my models:
class User
#OneToMany (cascade = { CascadeType.REFRESH, CascadeType.DETACH }, fetch = FetchType.EAGER)
private Set<Judgement> judgements;
class Judgement
#ManyToOne(cascade = { CascadeType.REFRESH, CascadeType.DETACH})
#JoinColumn(name = "user_id")
private User judge;
and in DB, i have to tables as Users and Judgements, when i tried to run my code, it showed error as:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "users_judgements" does not exist
does that mean i have to create the table users_judgements by hand, jpa cannot automatically create the relationship for me? RoR can do it...Thanks.
If you have a foreign key from USER table, user_id, in JUDGMENT table then there is no need to have another table. That will be the #JoinColumn(name = "user_id").
See if you are missing something; I can not see any "mappedBy" attribute, which will be "user" in your case.
Please take a look at this article; page 4 that the link will take you, give details about one-to-many relationship. It will be worth reading the whole article.
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa2.html?page=4
Also, the part 1 of the same tutorial is good for basics;
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html

Advanced JPA Mapping with Compound keys - OneToMany relationship

I am using JPA 1.0 and have following tables, namely, Type, Guide and Address (names simplified for clarity and highlighted in bold) It is a scenario, where in relationships between 3 tables is built on compound keys. Non key fields for every table are below the solid line.
Relationships
Type One --------> Many Guide
Guide Many <---------- One Address
Type
Code PK
Date1 PK
Name
Guide
Code FK
Date1 FK
Addr Identifier FK
Date2 FK
Value
Address
Addr Identifier PK
Date2 PK
Postal Code
(Pardon the formatting issue above)
I would like to start with Type table and unsing the compound key Code and Date1, get multiple rows (as a list) from Guide table. Then using Addr Identifier and Date2 from the row I want to get a single row in Address table. Please note these are reference tables and the data does not change, so there are no deletes or updates on any of these tables
I have tried this simple set of annotations that are returning empty list. (code is implified for clarity)
1)
#Entity
#Table(name = "Type")
public Class Type
#OneToMany(mappedBy = "type", fetch = FetchType.EAGER)
private List<Guide> listGuide;
getListGuide() {
return listGuide;
}
2)
#Entity
#Table(name = "Guide")
public Class Guide
#ManyToOne
#JoinColumns({#JoinColumn(name = "Code"),
#JoinColumn(name = "Date1") })
private Type type;
When I use getListGuide() I am getting an empty list.
Can you please suggest a solution?
I also need a mapping solution between Guide and Address entities.
Regards,