I have this Parent class
#Entity
#Table(name = "category")
#NamedQuery(name = "category.findAll", query = "SELECT c FROM Category c")
public class Category implements Serializable {
public Category(){}
#Column(name = "name", nullable = false)
#Id
private String name;
#Column(name = "col2")
private Boolean col2;
}
And i have referenced the parent table in child table as follows:
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "cat_name")
private Category category
when i run this JPQL query
update Category c SET c.name=:newName ,c.termsCanHaveChildren=:canHaveChdrn where c.name=:oldName
it's return with foreign key constraint error while i have put Cascade All in child field
Cannot delete or update a parent row: a foreign key constraint fails (`terms`.`term`, CONSTRAINT `FKaykenypxci167nqioh4xx9p3a` FOREIGN KEY (`cat_name`) REFERENCES `category` (`name`))
The problem lays at the constraint being generated by your persistence provider (hibernate), for the #JoinColumn(name = "cat_name") at the child table (and not with the CascadeType that you're defining)...
The generated constraint should indicated that when the PK of Category is Updated, any reference to such column should be updated also...
I believe this configuration should work (but you need to test it first, because I always generated my database model using scripts and not using hibernate features):
#ManyToOne
#JoinColumn(
name = "cat_name",
foreignKey = #ForeingKey(
name = "fk_child_category",
foreignKeyDefinition = "FOREIGN KEY (cat_name) REFERENCES category ON UPDATE CASCADE"
)
)
private Category category;
Also you need to check if your database supports "ON UPDATE CASCADE"... According to this link, oracle does not... (What database are you using?)
If this does not work, try the suggestion of Michelle...
That's expected: you are changing the Primary Key (#Id), that's used in a Foreign Key (#JoinColumn).
Use a surrogated immutable primary key.
Related
I have one-to-one mapping JPA table in my springboot application which works fine.
The Users is the parent table and in the account_no column, it stores the foreign key. Which is, child's primary key. The child is Account table.
However, when the application is started, I can see that there is one additional column (user_id) that has been created in H2 DB. I think it is something wrong with my JPA mapping. Pls help to figure it out. Below are the two classes.
#Entity
public class User extends AbstractEntity {
// Other fields related to user entity go here ..
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "account_no", referencedColumnName = "account_num")
private Account account;
}
#Entity
public class Account extends AbstractEntity{
// fields like account#, balance etc goes here..
#Column(name="account_num", unique = true)
#NotNull
private long accountNo;
#OneToOne (fetch = FetchType.LAZY)
private User user;
}
Startup log.
create table account (id bigint not null, account_num bigint not null, bal float not null, user_id bigint, primary key (id))
2021-12-22 00:09:28.765 DEBUG 25380 --- [ main] org.hibernate.SQL :
Decide which side should contain the extra column and use the mappedBy attribute. Then JPA will do what's needed
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, mappedBy = "user")
private Account account;
Considering that you have bidirectional mapping you don't need the #JoinColumn that you have used.
Just both #OneToOne annotations and the decision which would be owner entity of the relation by using the mappedBy attribute on one of those annotations.
It depends on you in which side you store the extra column which was a
foreign key.Because When You started to established oneToOne
relationship between two table it is mandatory to store user's
primary key in account table as a foreign key, or account table
primary key in user table as foreign key.You should be need to declare
mappedBy and your tables reference name.for example if you declare
mappedBy="user" in account table it create an extra account_id column
in user table as a foreign key, Same as for account
#OneToOne(optional = false, mappedBy = "user")
private Account account;
Don't need to declare these things in side OneToOne annotation cascade
= CascadeType.ALL, fetch = FetchType.LAZY, By default it always support lazy fetch
.
UPDATE: Changed code to be coherent to db model and changed details of the model itself. Also added code of the section that causes the error.
I am trying to implement a small database consulting system in Hibernate with PostgreSQL and having issues with one specific pair of tables. As you can see, it's a system for car rental services, and the tables store drivers and rentals. A driver is supposed to be able to have multiple rentals (but not the other way around).
Problem Tables
CREATE TABLE Driver(
cod_driver SERIAL PRIMARY KEY,
cod_client INTEGER,
num_license BIGINT UNIQUE,
expiration_license DATE,
ident_driver BIGINT,
FOREIGN KEY (cod_client)
REFERENCES Client(cod_client)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE Rental(
cod_rental SERIAL PRIMARY KEY,
cod_plate VARCHAR(10),
cod_dest VARCHAR(10),
cod_driver INTEGER,
date_delivery DATE,
FOREIGN KEY (cod_plate)
REFERENCES Vehicle(cod_plate)
ON UPDATE CASCADE
ON DELETE NO ACTION,
FOREIGN KEY (cod_dest)
REFERENCES Location(cod_location)
ON UPDATE CASCADE
ON DELETE NO ACTION,
FOREIGN KEY (cod_driver)
REFERENCES Driver(cod_driver)
ON UPDATE CASCADE
ON DELETE CASCADE
);
I did my implementation using Hibernate as follows (short of getters/setters for brevity):
Driver
#Entity
public class Driver {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer cod_driver;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cod_client")
private Client client;
private Long num_license;
private Long ident_driver;
private LocalDate expiration_license;
}
Rental
#Entity
public class Rental {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer cod_rental;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cod_plate")
private Vehicle vehicle;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cod_dest")
private Location location_dest;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cod_driver")
private Driver driver;
private LocalDate date_delivery;
Context of PSQLException
Persist function call in Main.java (clientGet is obtained through successfull queries, and inserts is just a class for queries):
Driver d = new Driver(clientGet, 3294324792L, 321312931L, LocalDate.of(2030, 10, 01));
inserts.insertEntity(d);
insertEntity function:
public void insertEntity(Object o) // Basic insertion of any persistent object
{
em.getTransaction().begin();
em.persist(o);
em.getTransaction().commit();
}
Error
The error I get is this:
Caused by: org.postgresql.util.PSQLException:
ERROR: insert or update on table "driver" violates foreign key
constraint "fkdfq0qhvpkw1dqguk6dv1dsj0t"
Detail: Key (cod_driver)=(3) is not present in table "rental".
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2497)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2233)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:310)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:446)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:370)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:149)
at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:124)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
What I've Considered
From my understanding, the relationship didn't need to be bidirectional (though I did try to use mappedBy), given that I don't store rentals in driver table.
I just don't understand what constraint could possibly be violated by this. It's as if it expects the value of cod_driver to be already in the rentals table, but a rental entity depends on the pre-existence of the driver existence. SQL for the database doesn't seem to have any constraint like that.
Can someone help me understand what I'm doing wrong? I tried all things I found, but nothing shed any light on this.
use many to many relationship between rental and driver.the same problem i faced once in my PurchaseOrder and PaymentMethods relationship.
#ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "rental_driver", joinColumns = #JoinColumn(name = "rental_id"), inverseJoinColumns = #JoinColumn(name = "driver_id"))
private List driver = new List();
I have two tables, Projects and TransitionAction. Both Projects and TransitionAction have a column request_no which is used perform join between them. The entity classes are as below:-
Project.java
#Entity
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(schema = "public", name="project_id_seq_gen",sequenceName="project_id_seq",initialValue=1,allocationSize=1)
#GeneratedValue(strategy= GenerationType.SEQUENCE,generator="project_id_seq_gen")
private Integer id;
#Column(name = "request_no")
private String request_no;
#Column(name = "title")
private String title;
#Column(name = "department")
private String department;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "requestNo")
private Set<TransitionAction> tacts;
#ManyToOne
#JoinColumn(name = "status_id")
private Status status;
#ManyToOne
#JoinColumn(name = "level_id")
private ProjectLevel level;
TransitionAction.java
#Entity
#Table(name = "transitionaction")
public class TransitionAction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "request_no")
private String request_no;
#Column(name = "actionDate")
private Date actionDate;
The code used to retrieve the list of projects as follows:-
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
UserInfo findByUserName(String userName);
#Query("SELECT project FROM Project project Join project.tacts pta where project.request_no= pta.request_no and project.status.id=1")
List<Project> getAllUserProjects();
}
I am getting could not extract ResultSet error. When I checked console, i found the following query being generated:
select
distinct project0_.id as id1_1_,
project0_.department as departme2_1_,
project0_.level_id as level_id6_1_,
project0_.user_nodal_officer as user_nod3_1_,
project0_.request_no as request_4_1_,
project0_.status_id as status_i7_1_,
project0_.title as title5_1_
from
project project0_
inner join
transitionaction tacts1_
on project0_.id=tacts1_.request_no
I am not getting why project. id is joined with tact.request_no and creating the error
Operator does not exist: integer = character varying
So you want to get all TransitionAction linked to Project by requestNo
You can achieve this by
adding #ManyToOne Mapping in TransactionAction like this
#ManyToOne(fetch = FetchType.LAZY)
private Project project;
Now you need to modify your query like this
#Query("SELECT project FROM Project project where project.status.id=1")
List<Project> getAllUserProjects();
To get all TransactionAction for a given project
Set<TransitionAction> allTatcts = project.getTacts();
You dont need to add join in query. Hibernate will take care of that once you pull TransactionAction from Project by an entity.
Edit 1:
But why did my query fail ? Why primary key of project joined with
request_no of TransitionAction ?
Your query failed because while doing #OneTOMany relation yodidn'tnt define #JoinColumn or #JoinTable which is a unidirectional mapping.
In this case, Hibernate will use primarykey column to map.
And since type of primarykey and column is different thus the error.
Without describing any physical mapping (no #JoinColumn or
#JoinTable), a unidirectional one to many with join table is used. The
table name is the concatenation of the owner table name, _, and the
other side table name. The foreign key name(s) referencing the owner
table is the concatenation of the owner table, _, and the owner
primary key column(s) name. The foreign key name(s) referencing the
other side is the concatenation of the owner property name, _, and the
other side primary key column(s) name. A unique constraint is added to
the foreign key referencing the other side table to reflect the one to
many.
Refer Official doc for more detail
#OneToMany
#JoinTable(
name="table_name",
joinColumns = #JoinColumn( name="columnname")
)
private Set<TransitionAction> tacts;
It is because you are having #Id on request_no in your TransactionAction class and when you join two class then they join on primary keys.
You can use cross join instead.
SELECT project FROM Project project, TransactionAction pta where project.request_no= pta.request_no and project.status.id=1
Or you can have bidirectional mapping. Add the following in your TransactionAction entity class.
#ManyToOne
private Project Project;
And then your query will be as follows.
select pta.project from TransactionAction pta where pta.request_no=pta.project.request_no and pta.project.status.id=1
I'am struggling to get Hibernate (with MySQL) to generate the primary key for a reference table from a "main table". My problem is that I got a big table with 25 mil rows and now I need to add multiple additional columns and because in the future where will be even more columns to add I choose the way to work with reference tables instead of adding the columns to the main table (the rebuild takes hours... :)
So there is a main table and a reference table. In my conception the primary key of the reference table should be generated from the primary key of the main table. I could first insert an entry into the main table, then select it and use its primary key for the insert into the reference table, but this seems not to be the best way to me. So I would like to use Hiibernate's generators, but I can't figure out how.
Here's the main table:
#Entity
#Table
public class Task {
#Id
#GeneratedValue
#Column()
private Integer id;
// ...
#OneToOne(mappedBy = "task_ref", orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Stuff stuff;
// ...
}
And the reference table:
#Entity
#Table
public class Stuff {
#Id
#Column(name = "stuff_id")
#GeneratedValue()
private Integer stuff_id;
// ...
#OneToOne(orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Task task_ref;
// ...
}
So.. how can I use a generator to create the primary key for the table Stuff from the primary key of the table Task ?
Any suggestions or other solutions are highly welcome!
Thanks!
This is how you should map your bidirectional OneToOne association with a shared primary key:
#Entity
#Table
public class Task {
#Id
#GeneratedValue
private Integer id;
// ...
#OneToOne(mappedBy = "task_ref", orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Stuff stuff;
// ...
}
#Entity
#Table
public class Stuff {
#Id
#Column(name = "stuff_id")
private Integer stuff_id;
// ...
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "stuff_id")
private Task task_ref;
// ...
}
Only the parent needs to cascade to the Child entity, not the other way around.
The Parent only has an "inverse" side of the association.
The shared primary key is both a primary key and a foreign key in the Child entity
The MapsId annotation allows you to share the primary key for both the #Id and the OneToOne association
You could use the Task object as your id, see EmbeddedId or IdClass for an example. In case Stuff represents a subclass of the Task entity in your domain model, you should model this entity as such, in which you will have to represent the Stuff entity as a Joined Subclass of the Task entity. In both cases the extra Id in the Stuff entity is not needed anymore.
I have a situation that is quite similar to the one outlined in this question's diagram: JPA. JoinTable and two JoinColumns, although with different issues.
I have three tables: Function, Group, and Location. Currently, I have a join table set up between Location and Group using #JoinTable. It is #ManyToMany on both sides, and works perfectly fine.
I am attempting to add the constraint that no Location should be associated with more than one Group that has the same Function. So I added a column for Function to my join table in my SQL schema and a uniqueness constraint across the Location and Function columns, like so:
create table function_table (
id varchar(50),
primary key(id)
);
create table group_table (
id varchar(50),
function_id varchar(50) not null,
primary key(id)
);
alter table group_table add constraint FK_TO_FUNCTION foreign key (function_id) references function_table;
create table location_table (
id varchar(50),
primary key(id)
);
create table group_location_join (
location_id varchar(50) not null,
group_id varchar(50) not null,
function_id varchar(50) not null,
primary key(location_id, group_id, function_id),
unique(location_id, function_id)
);
alter table group_location_join add constraint FK_TO_LOCATION foreign key (location_id) references location_table;
alter table group_location_join add constraint FK_TO_GROUP foreign key (group_id) references group_table;
alter table group_location_join add constraint FK_TO_FUNCTION foreign key (function_id) references function_table;
I then attempted to set up the following in my model entities:
#Entity
#Table(name = "function_table")
public class Function {
#Id
#Column(name = "id", length = 50)
private String id;
}
#Entity
#Table(name = "group_table")
public class Group {
#Id
#Column(name = "id", length = 50)
private String id;
#ManyToOne
#JoinColumn(name = "function_id", referencedColumnName = "id", nullable = false)
private Function function;
#ManyToMany
#JoinTable(name = "group_location_join",
joinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id"),
#JoinColumn(name = "function_id", referencedColumnName = "function_id")},
inverseJoinColumns = #JoinColumn(name="location_id", referencedColumnName = "id"))
private Set<Location> locations;
}
#Entity
#Table(name = "location_table")
public class Location {
#Id
#Column(name = "id", length = 50)
private String id;
#ManyToMany
#JoinTable(name = "group_location_join",
joinColumns = #JoinColumn(name="location_id", referencedColumnName = "id")
inverseJoinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id"),
#JoinColumn(name = "function_id", referencedColumnName = "function_id")})
private Set<Group> groups;
}
(Obviously, there is more to these entities, but I stripped them down to only the parts relevant to this question.)
This does not work. When I write a simple test to create a Location associated with a Group that is associated with a Function, the minute I try to flush the session to commit the transaction, Hibernate gives me this:
java.lang.ClassCastException: my.package.Group cannot be cast to java.io.Serializable
I think what's happening is that Hibernate is getting confused, throwing up its hands, and saying "I'll just serialize it, send it to the database, and hope it knows what's going on."
When I add implements Serializable and add a serialVersionUID to Group, I then get this:
org.hibernate.exception.SQLGrammarException: user lacks privilege or object not found: FUNCTION_ID
I'm not really sure how to proceed at this point, or if perhaps I have already proceeded too far down the wrong path. Maybe I'm not thinking about the SQL correctly, and there is a much easier way to ensure this constraint that doesn't involve all this ridiculousness.
Edit: In my system, the DAOs for the tables involved have no save capabilities. Which means that as long as my constraint is set up in the database, my application doesn't care; it can't insert things that violate the constraint because it can't insert things at all.
Edit 2: I never originally solved the stated problem, and instead simply added a third column in my database schema without touching the Java code, as stated in my first Edit section above. But I have since experimented with creating an explicit join table object with an #Embedded compound key, and it seems to work.
You are trying to create a composite primary key. In Hibernate you can do it using the #Embeddable annotation. In the example below you can find the way to use a composite key for two entities.
I believe you can move forward with this example and create your own version of primary key.
Mapping ManyToMany with composite Primary key and Annotation: