I have two object. Let it be company and employee.
CREATE TABLE company (
company_id BIGINT(20) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
chief_id BIGINT(20) NOT NULL,
PRIMARY KEY (company_id),
CONSTRAINT fk_company_chief
FOREIGN KEY (chief_id)
REFERENCES employee (employee_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
CREATE TABLE employee(
employee_id BIGINT(20) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
company_id BIGINT(20) NOT NULL,
PRIMARY KEY (employee_id),
CONSTRAINT fk_employee_company
FOREIGN KEY (chief_id)
REFERENCES employee (company_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
And my classes look like:
class Employee {
long id;
String name;
Company company;
}
class Company{
long id;
String name;
Employee chief;
}
Then I want to delete company with all its employees. I do it in a single transaction. I am getting smth like "java.sql.BatchUpdateException: Column 'chief_id' cannot be null"
I am able to delete only after making one of the columns nullable. For example "chief_id BIGINT(20) NULL," and then making company.chief=null before delete.
In the project we don't use Hibernate cascades and I am not able to change Database cascades.
We are using MySql 5.0.
I need smth like: disable constraints->remove entity->enable constraints. The disabled state should be accessible only within current transaction. I thought it was default behavior.
If your DBMS supports it, you can declare one of your constraints as deferrable initially deferred, so that it would be checked at the end of transaction.
You should use Hibernate cascades. Disabling/enabling database constraints is a DDL operation and it is committed immediately, there is no way to hide the "disabled state" from other transactional contexts.
If you cannot use Hibernate cascades, what you could do is use a dummy company (company_id=-1 for instance) and a dummy chief Employee object (employee_id=-1). DummyChief belongs to the company DummyCompany, and DummyCompany's chief is DummyChief.
You could then proceed in the following order:
1/ Delete all non-chief Employees in the Company.
2/ Set the Company chief employee to DummyChief (employee_id=-1)
3/ Delete the Chief employee.
4/ Delete the Company company.
Related
I have followin sql:
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`power_id` int(11) NOT NULL
)
ALTER TABLE `user`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `power_id` (`power_id`),
ALTER TABLE `user`
ADD CONSTRAINT `FK_user_power` FOREIGN KEY (`power_id`) REFERENCES `power` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE TABLE `power` (
`id` int(11) NOT NULL,
`updated_date` date NOT NULL
)
( its hand typed )
However when i do view Persistence -> Generate Persistance Mapping -> By Database Schema It generated this in User entity class:
private PowerEntity powerByPowerId;
#ManyToOne
#JoinColumn(name = "power_id", referencedColumnName = "id", nullable = false)
public PowerEntity getPowerByPowerId() {
return powerByPowerId;
}
which is clearly 1:n relation ( e.g multiple users have one power ) while sql says about 1:1 relation.
NOw i am unsure if my sql is wrong or intelij generated wrong relatios.
How to deal with it? My database schema is not small and so far i have noticed only this mistake, however i am not sure if its my fault or intelij's mapping is producing wrong entity tables ( which would mean i could find another badly generated entity ).
Thanks for help.
In hibernate I have created a one-to-many relationship between user and roles.
More then one user can be associated with a role, but a user can be associated with just one role.
In User class, I have
#ManyToOne
#JoinColumn(name = "role_id")
private Role role_id;
In Role Class, I have
#OneToMany(mappedBy="role_id", fetch=FetchType.LAZY)
private Collection<User> userList = new ArrayList<User>();
If I am saving a new user as :
{
"userName" : "Michael",
"role_id" : {
"id" : 8
}
}
It saves user even when role table has no role with id as 8( and no entry is done in role table).
I want that I should get a referential integrity error whenever I am saving a user with a non existent role.
What should I change?
My Users table is created as:
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`email` varchar(255) DEFAULT NULL,
`mobile` varchar(255) DEFAULT NULL,
`username` varchar(255) NOT NULL,
`role_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_abcdef` (`role_id`),
CONSTRAINT `FK_abcdef` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Do you have FK constraint in your tables in the database? Maybe this if you use mySql can help you. Or alternatively you can set the restriction in your database by your own.
In your #OneToMany annotation you probably need to add targetEntity = User.class. That is because of the following reasons:
java generics use type erasure, which means that in the generated bytecode, your userList is of type Collection, not of type Collection<User>. So, hibernate has no clue what the target entity is, unless you explicitly tell it.
Hibernate generally likes to follow a silent error / hidden error approach, which means that it silently ignores problems the moment they happen (as for example the moment where it has no clue what type your collection elements are) and to fail later, when the cause of the problem is not very clear anymore. (As for example when you execute a query which should fail, and it doesn't, or you execute a query which should fetch something, and it fetches nothing.)
As of java 9, the compiler does actually store information about the actual type arguments used when creating generic fields within the generated .class files, and it is possible to discover that information using reflection, but I am not aware of hibernate having been updated to check this information.
Let's say I have an object
class User
{
int id;
String name;
String password;
List<Property> properties;
}
And
class Property
{
int id;
String key;
String value;
}
The table in MySQL however is slightly different.
CREATE TABLE `user` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45),
`password` VARCHAR(45),
PRIMARY KEY (`id`));
And
CREATE TABLE `property` (
`id` INT NOT NULL AUTO_INCREMENT,
`key` VARCHAR(45) NULL,
`value` VARCHAR(45) NULL,
`userId` INT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `userId`
FOREIGN KEY (`userId`)
REFERENCES `user` (`userId`));
How would I create hibernate annotations so that if I would like to save user object I will end up saving/creating new properties accordingly?
I really do not want to put User object inside Property object for this to happen.
You can achieve this using unidirectional OneToMany mapping, but it will create an extra table with userId and propertyId column and your property table will not need userId column. In this case you will be able to get all properties of user, but you won't be able to query a particular property belongs to which user.
If you want to have user information in a property object, you must use bi-directional OneToMany mapping and for that you will need put user inside Property class.You can control the initialization of User object inside Property class while fetching data from database by configuring fetch type for User. By default, fetch type is LAZY, meaning User object will fetched from database only when you use it.
I am having a problem that hibernate tries to drop foreign keys that dont exist instead of the one that exists. My scenario looks like this.
I want to run a junit tests, before ever test I want to create DB and after ever test I want to drop it. For that I use hibernate create-drop property. However the tricky part is that I want to create my own tables as a way to test newly added sql and verify that it will run fine once I deploy it to the production db server. So what happens is this
Hibernate creates tables automatically
Hibernate creates foreign key relationships
Hibernate runs my drop table scripts (that succeeded since there is no data so no foreign key rule has been broken)
Hibernate runs my create table scripts
Hibernate runs my add foreign constraint scripts
Hibernate runs my insert data scripts
Test is executed
Hibernate tries to remove the foreign key and it fails.
The reason hibernate has not be able to remove it is cause it tried to remove that one that hibernate created and not the one that was created by my scripts.
Any idea how to force hibernate to find out the actual foreign key? Any way to get around this problem?
Thanks everyone
Class for which hibernate creates the table
TodoGroup.java
#Entity
#Table(name = "ToDoGroups")
public class ToDoGroup implements Serializable{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name", length = 50)
private String name;
#ManyToOne
#JoinColumn(name = "UserSettingsId")
#XmlTransient
private UserSettings userSettings;
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL)
private List<ToDoItem> items;
hibernate adding the constraint
alter table ToDoGroups
add constraint FK790BA1FAFE315596
foreign key (UserSettingsId)
references UserSettings
running my own tables that work fine since there is no data so I can remove what hibernate created in order to verify my sql
DROP TABLE IF EXISTS ToDoGroups;
CREATE TABLE ToDoGroups (ID BIGINT NOT NULL IDENTITY, Name VARCHAR(50) NOT NULL, UserSettingsId BIGINT NOT NULL, PRIMARY KEY (ID));
ALTER TABLE ToDoGroups ADD FOREIGN KEY (UserSettingsID) REFERENCES UserSettings (ID);
drop fk it tries to execute
alter table ToDoGroups drop constraint FK790BA1FAFE315596
java.sql.SQLException: Constraint not found FK790BA1FAFE315596 in table: TODOGROUPS in statement [alter table ToDoGroups drop constraint FK790BA1FAFE315596]
tries to remove the table which fails due to the constrain that I have set in my create.sql script
drop table ToDoGroups if exists
java.sql.SQLException: Table is referenced by a constraint in table SYS_REF_SYS_FK_808_810 table: TODOITEMS in statement [drop table ToDoGroups if exists]
Update
I have also noticed that hibernate when it first starts before it creates the tables (so this is way before my scripts are run), tries to remove foreign key in order to drop any table that exists.
So how does hibernate know what foreign key to use? It uses the same key that
first statement it executes
alter table ToDoGroups drop constraint FK790BA1FAFE315596
then it drops all of the tables
drop table ToDoGroups if exists
then it creates table
create table ToDoGroups (
id bigint generated by default as identity (start with 1),
Name varchar(50),
UserSettingsId bigint,
primary key (id)
)
then it adds the same FK
alter table ToDoGroups
add constraint FK790BA1FAFE315596
foreign key (UserSettingsId)
references UserSettings
I think my question here is how does hibernate know what FK to use. It used the same FK in the first drop statement when there was even no table. Later it used that some FK to create the relationship. Shouldn't hibernate first check if the table exists and then tries to determine what is the FK?
As far as I understand, your problem is that your own script and hibernate don't use the same constraint name.
You can specify a constraint name used by hibernate with this annotation on your relationship:
#ForeignKey(name = "fk_UserSettings")
And additionally, in your create.sql:
ALTER TABLE ToDoGroups ADD CONSTRAINT fk_UserSettings FOREIGN KEY (UserSettingsID) REFERENCES UserSettings (ID);
I think my question here is how does hibernate know what FK to use. It used the same FK in the first drop statement when there was even no table. Later it used that some FK to create the relationship. Shouldn't hibernate first check if the table exists and then tries to determine what is the FK?
The foreign key name used by hibernate is the concatenation of
"FK_" + hashcode of referenced entity name + hash code of referenced columns name on that entity.
So it is not a randomly generated key (you will see that it will change if you change your entity name). And that's how hibernate knows the name of the fk to drop (hibernate is expecting that the constraint was created by hibernate with this well known naming strategy).
Hibernate use the name of the constraint to manipulate it. It don't compare the "rule" coded in constraints associated with a table to see if the constraint is already there or not.
I have a JPA Mapping question to do.
We have a One-To-Many relationship between two entities (Sale and Pig). Classes follow at the final of this message to ilustrate.
'Sale' is a event on a 'Pig', like many others in the system ('Inspection' is another example).
However, 'Sale' is the only event who has One-To-Many relationship with 'Pig', the others has One-to-One.
So, to map all events of a 'Pig' we use a 'PigEvent' entity.
We save(insert) a 'PigEvent' object at the same time the user inserts a 'Pig' in the system.
We want to use this entity ('PigEvent') like the 'jointable' of Sale.getPigs() mapping.
But doing that, some problem occurs:
- when a new 'Sale' is inserted, hibernate try to insert new 'PigEvent' for every 'Pig' in the 'Sale'
(this generates a duplicate PK exception, because PigEvent already exists)
- when a new 'Sale' is deleted, hibernate deletes 'PigEvent' for every 'Pig' in the 'Sale'
(doing this we loose the others Events relationship data)
We understand that this is the normal behaviour of this kind of mapping (One-to-Many with jointable).
We want to know how configurate JPA/Hibernate to just load Sale.getPigs() (in SELECT´s),
but in INSERT, UPDATE, DELETE operations in 'Sale' don´t action at all in that mapping (Sale.pigs()).
We use Hibernate 3.6.2.
Thanks in advance.
#Entity
public class Pig extends Persistente implements Serializable {}
#Entity
public class PigEvent extends Persistente {
#OneToOne
#JoinColumn(name="idpig")
private Pig pig;
#OneToOne
#JoinColumn(name="idapproval")
private Inspection approval
#OneToOne
#JoinColumn(name="idsale")
private Sale sale;
}
#Entity
public class Inspection extends Persistente{
#OneToOne
#JoinColumn(name="idSuino")
private Pig pig;
}
#Entity
public class Sale extends Persistente{
#MOneToMany
#JoinTable(name="e33PigEvent",uniqueConstraints=#UniqueConstraint(columnNames="idpig"),
joinColumns={#JoinColumn(name="idsale",insertable=false,updatable=false)},
inverseJoinColumns={#JoinColumn(name="idpig",insertable=false,updatable=false)})
public Set<Pig> getPigs() {}
}
Table Structure:
CREATE TABLE `e33Pig` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
CREATE TABLE `e33PigEvent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`idPig` int(11) NOT NULL,
`idInspection` int(11) DEFAULT NULL,
`idSale` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idPig` (`idPig`),
CONSTRAINT `fk_e33eventossuino_e33aprovacao1` FOREIGN KEY (`idInspection`) REFERENCES `e33Inspection` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `fk_e33eventossuino_e33suino1` FOREIGN KEY (`idPig`) REFERENCES `e33Pig` (`id`),
CONSTRAINT `fk_e33eventossuino_e33venda1` FOREIGN KEY (`idSale`) REFERENCES `e33Sale` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);
CREATE TABLE `e33Sale` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
CREATE TABLE e33Inspection (
`id` int(11) NOT NULL AUTO_INCREMENT,
`idsuino` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_e33Inspection_e33suino1` FOREIGN KEY (`idPig`) REFERENCES `e33Pig` (`id`)
) ;
You can't use the same table (e33PigEvent) to map an entity (PigEvent) and an association (the OneToMany association). If the table is mapped to an entity, then you don't have a OneToMany association between Sale and Pig anymore: you have a OneToMany between Sale and PigEvent, mapped by a foreign key in e33PigEvent, and a OneToOne between PigEvent and Pig, also mapped by a foreign key in e33PigEvent.
If you map a OneToMany using a JoinTable, then Hibernate handles insertions and deletions in this table itself, each time you add or remove Pigs from the collection. Since you have additional columns in the join table, you need to create PigEvent instances yourself, and add these instances to the collection of events of the sale.