Hibernate create foreign key to itself - java

I have a very strange problem with hibernate at the moment.
Somehow on a table, it create a foreign key which reference to itself. the column is also a primary key. This essentially prevent me from delete any row from that table.
In the log I could see a line:
DEBUG org.hibernate.SQL - alter table Device add index
FK79D00A76C682495E (id), add constraint FK79D00A76C682495E foreign key
(id) references Device (id)
Other table with similar table seems fine. and this seems to be true for both MySQL and Derby. I'm using hibernate 4.1.4
the annotated class is as follow.
#Entity(name = "Device")
public class Device extends DomainObject implements Searchable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
#Column(name = "Type")
#Enumerated(EnumType.STRING)
private DeviceTypeEnum type = DeviceTypeEnum.AccessControlDevice;
#Column(name = "Name", length = Constance.DATABASE_NAME_FIELD_LENGTH)
private String name;
#Column(name = "Description", length = Constance.DATABASE_DESCRIPTION_FIELD_LENGTH)
private String description;
#Column(name = "Identifier", length = Constance.DATABASE_IDENTIFIER_FIELD_LENGTH, unique = true)
private String identifier;
#ManyToMany
#JoinTable(name = "Device2Group", joinColumns = {#JoinColumn(name = "DeviceID")}, inverseJoinColumns = {#JoinColumn(name = "DeviceGroupID")})
private List<DeviceGroup> groups = new ArrayList<DeviceGroup>();
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "location")
private Location location;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "Address")
private Address address;
#ManyToOne
#JoinColumn(name = "Link", nullable = false)
private Link link;
}

It turns out that in one of the Entity Class "Location" which the Device entity references, it has a #ManyToMany mapped collection of Device, where it really should be #OneToMany.
After change the #ManyToMany to #OneToMany the constrain disappears.

I see no references to Device class in your code. So I am assuming that this class has been modified, but its table has not, because it has some data. (Why else should it have a foreign-key to itself?)
Try dropping this table in your database to make hibernate create it once more, or set p.hibernate.hbm2ddl.auto to create-drop.

Related

How can I map these entities using hibernate? Need to map one to many relationship. Foreign key is not updating

In this case, I have 2 entities (Users table and UploadRecord table). I need to map a one-to-many relationship because one user can have many upload records. I need to use UserId as the primary key in the Users table and a foreign key as the UploadRecord table.
I tried using this code but the UploadRecord table fk_UserId is not updating. How to fix this issue?
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_UserId", referencedColumnName = "UserId")
private List<UploadRecord> uploadRecord;
I wrote Users entity class and UploadRecord entity class as follows.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name= "UserId")
private Long UserId;
#Column(nullable = false, unique = true, length = 45)
private String email;
#Column(name = "fullName", nullable = false, length = 20)
private String fullName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_UserId", referencedColumnName = "UserId")
private List<UploadRecord> uploadRecord;
//Getters and setters
#Entity
#Table(name = "uploadrecord")
public class UploadRecord {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long uploadRecordId;
#Column(nullable = false, unique = false, length = 1000)
private String fileName;
//Getters and setters
It seems you haven't finished modelling the relationship between these two entities.
Edit your models like this:
User:
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<UploadRecord> uploadRecords;
UploadRecord :
#ManyToOne
#JoinColumn(name = "userId")
private User user;
More details for modelling relations: Baeldung
Moreover keep an eye on naming convention:
UserId -> userId
uploadRecord -> uploadRecords (Lists, Sets, ...) -> plural

list only has one element

I'm using hibernate to read data from a database. I have 3 entities, sensor, zone and a N to N relationship between those two entities, that is the entity SensorZone.
For each entity I created a class, which are the examples that follow, they don't include a constructor, getters and setters:
#Entity
#Table(name = "Sensor")
public class Sensor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "zone")
private List<SensorZone> zones = new ArrayList<>();
}
#Entity
#Table(name = "Zone")
public class Zone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "max_count")
private int maxCount;
#Column(name = "current_count")
private int currentCount;
#Column(name = "created")
private Timestamp created;
#Column(name = "modified")
private Timestamp modified;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sensor")
private List<SensorZone> sensors = new ArrayList<>();
}
#Entity
#Table(name = "Sensor_Zone")
public class SensorZone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "zone_id")
private Zone zone;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "sensor_id")
private Sensor sensor;
#Column(name = "enter_exit")
private boolean enterExit;
}
I'm using PostgreSQL as a database engine, and the tables are as follow:
CREATE TABLE Sensor (
id SERIAL,
name TEXT NOT NULL UNIQUE,
CONSTRAINT PK_Sensor PRIMARY KEY (id)
);
CREATE TABLE Zone (
id SERIAL,
name TEXT NOT NULL UNIQUE,
max_count INT NOT NULL,
current_count INT NOT NULL,
created TIMESTAMP NOT NULL,
modified TIMESTAMP NOT NULL,
CONSTRAINT PK_Zone PRIMARY KEY (id)
);
CREATE TABLE Sensor_Zone (
id SERIAL,
zone_id INT NOT NULL,
sensor_id INT NOT NULL,
enter_exit BOOLEAN NOT NULL,
CONSTRAINT PK_Zone_Sensor PRIMARY KEY (id, sensor_id, zone_id),
CONSTRAINT FK_Zone_Sensor_Zone FOREIGN KEY (zone_id) REFERENCES Zone (id),
CONSTRAINT FK_Zone_Sensor_Sensor FOREIGN KEY (sensor_id) REFERENCES Sensor (id)
);
And here's the values on the table Sensor_Zone:
The problem is that the field zones from Sensor only has one element in the list, even there are multiple elements in the database.
I've tried to put FecthType.EAGER, but it didn't change anything.
Not the exact solution (a.k.a. Could your model be re-modeled?)
I know it's easier said than done, but if you could avoid managing extra column(s) on the joining table, you could resolve it using standard example of many-to-many with joining table.
Perhaps there is a way of thinking about your extra column so that it could become owned by either Zone or Sensor entities? Maybe it's complementary as in on one side of many-to-many if it is set to TRUE it means one thing, but when it's missing it is equivalent to being FALSE.
#Entity
#Table(name = "Sensor")
public class Sensor {
// ...
#OneToMany
#JoinTable(
name = "Sensor_Zone",
joinColumns = { #JoinColumn(name = "sensor_id") },
inverseJoinColumns = { #JoinColumn(name = "zone_id") }
)
private List<Zone> zones = new ArrayList<>();
}
#Entity
#Table(name = "Zone")
public class Zone {
// ...
#OneToMany
#JoinTable(
name = "Sensor_Zone",
joinColumns = { #JoinColumn(name = "zone_id") },
inverseJoinColumns = { #JoinColumn(name = "sensor_id") }
)
private List<Sensor> sensors = new ArrayList<>();
}
And then your joining table does not need to be an entity simplifying things.
The Solution (Here be dragons)
Escape hatch: for solution please check Mykong's blog
You have to look into using #AssociationOverrides on the entity created from joining table. Please also note a separate class annotated with #Embeddable created to deal with the composite key in the joining table.

Override the Foreign Key Name pointing to Composite Key JPA/Hibernate

I'm using spring-boot 1.5.4 with spring-data-jpa and I'm trying to override the auto generated foreign key name during spring.jpa.hibernate.ddl-auto=create.
For simple id, I was able to override it: simple_fk
Hibernate: alter table my_entity add constraint simple_fk foreign key (simple_id) references simple
But not for foreign key with composite id: FKms12cl9ma3dk8egqok1dasnfq
Hibernate: alter table my_entity add constraint FKms12cl9ma3dk8egqok1dasnfq foreign key (composite_id1, composite_id2) references composite
What is wrong with my code? I also tried #PrimaryKeyJoinColumn.
Please see the class definitions below.
#Entity
public class Simple {
#Id
private long id;
}
#Entity
public class Composite {
#Id
private CompositeId id;
}
#Embeddable
public class CompositeId {
#Column
private long id1;
#Column
private long id2;
}
#Entity
public class MyEntity {
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "simple_fk"),
name = "simple_id", referencedColumnName = "id")
private Simple simple;
#ManyToOne
#JoinColumns(foreignKey = #ForeignKey(name = "composite_fk"), value = {
#JoinColumn(name = "composite_id1", referencedColumnName = "id1"),
#JoinColumn(name = "composite_id2", referencedColumnName = "id2")
})
private Composite composite;
}
This is a known issue with the Hibernate which was fix in version 5.2.8
So there are two ways to fix it: Either you update the Hibernate to the version 5.2.8 or up by adding
<hibernate.version>5.2.10.Final</hibernate.version>
to your pom.xml, which basically will update the Hibernate to the latest version.
or if Hibernate update is not possible or is just too risky you can add legacy/deprecated #org.hibernate.annotations.ForeignKey(name = "composite_fk") annotation on your
composite field which will make you code look like
#Entity
public class MyEntity {
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "simple_fk"), name = "simple_id", referencedColumnName = "id")
private Simple simple;
#ManyToOne
#JoinColumns(foreignKey = #ForeignKey(name = "composite_fk"), value = {
#JoinColumn(name = "composite_id1", referencedColumnName = "id1"),
#JoinColumn(name = "composite_id2", referencedColumnName = "id2") })
#org.hibernate.annotations.ForeignKey(name = "composite_fk")
private Composite composite;
}

Why is #ManyToMany not working with non-primary key columns?

I have 2 entities - User and Role which have following relations: User has a manytomany relation to itself and a manytomany relation with the Role entity.
#Entity
public class UserEntity implements Serializable {
#Id
#Column(length = 12, columnDefinition = "BINARY(12)", name = "Id", unique = true)
private byte[] id;
#Column(name = "Login", unique = true, nullable = false)
private String login;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Login") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "User_User",
joinColumns = { #JoinColumn(name = "UserParent") },
inverseJoinColumns = { #JoinColumn(name = "UserChild") })
private Collection<UserEntity> children;
...
}
and Role:
public class RoleEntity implements Serializable{
#Id
#Column(name = "Id", unique = true, nullable = false)
private String id;
...
}
The strange thing about the setup of DB is that the User_User relation is based on the binary Id keys
create table if not exists User_User (
UserParent binary,
UserChild binary
);
and the user-role is based on varchars
create table if not exists KNUser_UserRole (
UserLogin varchar,
RoleId varchar,
);
Now, when it runs, the user-user relationship work well. However, when I try to access the collection returned for roles, I get a ClassCastException:
java.lang.ClassCastException: **.entity.UserEntity cannot be cast to [B
at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.extractHashCode(PrimitiveByteArrayTypeDescriptor.java:41)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:205)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:114)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:79)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:240)
at org.hibernate.engine.internal.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:740)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1181)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:800)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:651)
at org.hibernate.loader.Loader.doQuery(Loader.java:856)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2175)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:622)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1606)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
It looks like the UserEntity is being cast to some binary(?) thing. However, the first relation between users themselves works fine, but the one with another table is wrong.
I am using different columns of different types to join tables. Is it allowed to do it this way?
Another strange thing is that when I switch the #Id annotation to be on the login field, the roles work fine, no issue, but then of course the self-join PersistentBag key is the Login instead of Id, which breaks the relation and no results are retrieved. But the conversion from UserEntity to the "[B" is not done.
Also if I leave things as in example and change the Id type to String (and the DB to varchar) it also starts working (of course not consistently with the User_User table).
What am I doing wrong? What is the reason for getting the classcastexception in this case? Why it work when I change the byte[] to String? Please let me know if you have any ideas. I do not want to change the DB design cause this would lead to lots migration and compatibility issues for clients already using the DB.
Just a note: the #Id has to be on the Id binary field as otherwise I wouldn't be able to make a self-join (I was unable to point twice to a column not being a primary key see: Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException).
Cheers
Adam
the referred column in your join table must be unique entry, here if you put #Id on login field then it works fine,but when you change it to different other than #Id column you cant be sure about the entries will be unique.what you can do is,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
I think it should work.

Why is this causing java.sql.SQLIntegrityConstraintViolationException?

My app has an entity class (Ativo) with this attribute and JPA annotations:
#JoinColumn(name = "BOLSA", referencedColumnName = "ID")
#ManyToOne(optional = false, cascade = {CascadeType.PERSIST})
private Bolsa bolsa;
When I try to persist the entity class (Ativo), this exception is thrown:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL131102225757700' defined on 'BOLSA'.
I don't understand what is wrong with my code. Why is it trying to create a new object of type Bolsa if this should be just a foreign key to an existing object??
Header of class Ativo:
#Entity
#Table(name = "ATIVO")
public class Ativo implements EntityInterface<Ativo>, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#Basic(optional = false)
#Column(name = "CODIGO", unique=true, nullable = false)
private String codigo;
#Basic(optional = false)
#Column(name = "TIPO_MERCADO", nullable = false)
private String tipoMercado;
#Column(name = "DESCRICAO", nullable = false, length = 10000)
private String descricao;
#JoinColumn(name = "BOLSA", referencedColumnName = "ID")
#ManyToOne(optional = false, cascade = {CascadeType.PERSIST})
private Bolsa bolsa;
Header of class Bolsa:
#Entity
#Table(name = "BOLSA")
public class Bolsa implements EntityInterface<Bolsa>, Serializable, Comparable<Bolsa> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#Basic(optional = false)
#Column(name = "NOME", unique = true, nullable = false)
private String nome;
#Column(name = "DESCRICAO", nullable=false, length = 10000)
private String descricao;
The error message is explicit: you're trying to insert a row with an id that already exists in the data base, hence an integrity constraint violation (a duplicate primary key) ensues.
Check your code, the part where the id is being set, and make sure that it uses an unique identifier. Or use annotations in the id field to auto generate a new key each time. Or define a database sequence to do this.
as suggested in the comments, removing cascade = {CascadeType.PERSIST} from the below relation will work.
#JoinColumn(name = "BOLSA", referencedColumnName = "ID")
#ManyToOne(optional = false, cascade = {CascadeType.PERSIST})
private Bolsa bolsa;
The reason is, Cascade Persist take care of getting things put into the database for you via transitive persistence, so you don't have to explicitly persist the parent (in your case 'Bolsa'). However, it is always your responsibility to maintain the in-memory state of the objects before persisting.
I am suspecting (as it is not clear from the provided snips) that you are explicitly persisting Bolsa object before saving Ativo, so the exception is resolved by removing the cascade persist constraint.
The error message gives you enough information to keep digging:
a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL131102225757700'
It states the it is either a primary key or a unique key constraint, and given the unique-constraints indicated by your mappings (and the fact that I can't see anything wrong with the mappings themselves), I would guess that it a unique key constraint violation, for instance the nome field. But, the error message gives us something more, and that is the name of the constraint that is being violated: SQL131102225757700. So, what you need to do is to figure out which column this constraint applies to, and once you do I'm guessing you will be a lot closer to figuring out the error. (Exactly how to do this depends on your database provider, google it..)

Categories

Resources