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

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;
}

Related

HIbernate doesn't create Foreign key for #OneToOne

I have two tables:
#Entity
public class TestEntity {
#Id
#Column(name = "id")
private UUID id;
#OneToOne(targetEntity = InfoEntity.class, cascade = CascadeType.ALL)
#JoinColumn(name="id", referencedColumnName = "id")
private InfoEntity info;
...
}
#Entity
public class InfoEntity {
#Id
#Column(name = "id")
private UUID id;
#OneToOne(mappedBy = "info")
private TestEntity test;
...
}
Basically I want to define a Foreign Key from TestEntity to InfoEntity by id fields which are the same in both tables. The problem is that when I check the database in IntelliJ Idea I don't see any Foreign Keys in keys section (checked both tables), only their PK's. Is something wrong with this code? I already set the property as it suggested in another similar question:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
The problem is that name in JoinColumn has value id. Change it to info_id for example:
#OneToOne(targetEntity = InfoEntity.class, cascade = CascadeType.ALL)
#JoinColumn(name="info_id", referencedColumnName = "id")
private InfoEntity info;
You TestEntity already has a column id, so it has to has another name.

JPA Relationship issue

I'm working on a project, and I encountered a problem with the JPA relationship. I've been advised on another thread to change a couple of things, however, I still can't get it to work properly.
I'm getting an exception and I know where the problem is but not sure how to solve it.
here is the User class:
#Entity
#Table(name = "user")
public class UserModel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "username", nullable = false)
private String username;
#Column(name = "password", length = 500, nullable = false)
private String password;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Car> cars;
}
here is the Car class:
#Entity
#Table(name = "car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(length = 11)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "make", nullable = false)
private String make;
#Column(name = "model", nullable = false)
private String model;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "id") //here is where the exception throws (duplicated ID or com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'user_model_id' in 'field list' if I change that to user_model_id.
private UserModel userModel;
}
here is the service impl:
#Component
#Transactional(readOnly = true)
public class CarServiceImpl implements CarService {
#Inject
private CarRepository carRepository;
#Inject
private UserRepository userRepository;
#Override
#Transactional(readOnly = false)
public Car addCar(Long userId, Car car) {
User user = userRepository.findOne(userId);
user.getCars().add(car);
car.setUser(user);
carRepository.save(car);
return car;
}
Any help will be really much appreciated.
Thank you so much
I think mappedBy needs to point to the field name that owns the relation. In this case this is userModel so it should be
#OneToMany(mappedBy = "userModel", cascade = CascadeType.ALL)
If the relationship is bidirectional, the mappedBy element must be
used to specify the relationship field or property of the entity that
is the owner of the relationship. (from https://www.objectdb.com/api/java/jpa/OneToMany)
Also I think that your #JoinColumn annotation is wrong. It should specify the column used to join the related entity - so it cannot be ID but something like user_id
See https://www.objectdb.com/api/java/jpa/JoinColumn#
Also your ddl is wrong - this piece says that cars and users have the same id - which you do not want - your ddl is missing the actual column for the user foreign key
FOREIGN KEY (id)
REFERENCES game.user (id)
So if you changed JoinColumn to #JoinColumn(name = "user_id") your ddl for the foreign key must be.
CREATE TABLE game.car (
id INT(11) NOT NULL AUTO_INCREMENT COMMENT '',
make VARCHAR(15) NOT NULL COMMENT '',
model VARCHAR(15) NOT NULL COMMENT '',
PRIMARY KEY (id) COMMENT '',
user_id INT(11),
CONSTRAINT fk_user
FOREIGN KEY (user_id)
REFERENCES game.user (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)
If I can add my 2 cents to the mapping - try to avoid bidirectional relations wherever you can.

JPA mapping #ManyToOne between Embeddable and EmbeddedId

I have the following setup in my Spring Boot JPA application:
Embeddable
#Embeddable
public class LogSearchHistoryAttrPK {
#Column(name = "SEARCH_HISTORY_ID")
private Integer searchHistoryId;
#Column(name = "ATTR", length = 50)
private String attr;
#ManyToOne
#JoinColumn(name = "ID")
private LogSearchHistory logSearchHistory;
...
}
EmbeddedId
#Repository
#Transactional
#Entity
#Table(name = "LOG_SEARCH_HISTORY_ATTR")
public class LogSearchHistoryAttr implements Serializable {
#EmbeddedId
private LogSearchHistoryAttrPK primaryKey;
#Column(name = "VALUE", length = 100)
private String value;
...
}
OneToMany
#Repository
#Transactional
#Entity
#Table(name = "LOG_SEARCH_HISTORY")
public class LogSearchHistory implements Serializable {
#Id
#Column(name = "ID", unique = true, nullable = false)
private Integer id;
#OneToMany(mappedBy = "logSearchHistory", fetch = FetchType.EAGER)
private List<LogSearchHistoryAttr> logSearchHistoryAttrs;
...
}
Database DDLs
CREATE TABLE log_search_history (
id serial NOT NULL,
...
CONSTRAINT log_search_history_pk PRIMARY KEY (id)
);
CREATE TABLE log_search_history_attr (
search_history_id INTEGER NOT NULL,
attr CHARACTER VARYING(50) NOT NULL,
value CHARACTER VARYING(100),
CONSTRAINT log_search_history_attr_pk PRIMARY KEY (search_history_id, attr),
CONSTRAINT log_search_history_attr_fk1 FOREIGN KEY (search_history_id) REFERENCES
log_search_history (id)
);
When I go to start the application, I get the following error:
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.foobar.entity.LogSearchHistoryAttr.logSearchHistory in com.foobar.entity.LogSearchHistory.logSearchHistoryAttrs
I am not sure why I am getting this error - the mapping looks correct (to me). What is wrong with this mapping that I have? Thanks!
You moved the mappedBy attribute into an embeddable primary key, so the field is no longer named logSearchHistory but rather primaryKey.logSearchHistory. Change your mapped by entry:
#OneToMany(mappedBy = "primaryKey.logSearchHistory", fetch = FetchType.EAGER)
private List<LogSearchHistoryAttr> logSearchHistoryAttrs;
Reference: JPA / Hibernate OneToMany Mapping, using a composite PrimaryKey.
You also need to make the primary key class LogSearchHistoryAttrPK serializable.
At OneToMany part:
#OneToMany(mappedBy = "primaryKey.logSearchHistory", fetch = FetchType.EAGER)
private List<LogSearchHistoryAttr> logSearchHistoryAttrs;

Share columns between composite key and #ManyToOne JoinColumns

I'm having a problem that I don't know if it's possible to solve just by using hibernate/jpa annotations. The problem is that I have a composite key that has the same column as one of my foreignkey composite id, and I would like to share this same column on the table. For example:
#Entity
class Id {
#Id
#Column(name = "idPessoa")
public Integer idShared;
}
#Embeddable
class APK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public String nKey;
}
#Entity
class A {
#EmbeddedId
public APK id;
}
#Embeddable
class BPK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public Integer nCode;
}
#Entity
class B {
#EmbeddedId
public BPK id;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "idShared", nullable = false, insertable = false, updatable = false), #JoinColumn(name = "nKey", nullable = false) })
public A a;
}
The question is how I can share the column idShared between A and B and use it in the #ManyToOne for the foreign key?
I already tried to use #JoinColumn inside #JoinColumns with the name idShared but I get an error saying that I need to use insert = false and update = false, I already put insertable = false and updateable = false, but then I get another error saying that I can't mix things.
I found a possible solution saying to use:
#ManyToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula = #JoinFormula(value = "idShared", referencedColumnName = "idShared")),
#JoinColumnOrFormula(column = #JoinColumn(name = "nKey", nullable = false)) })
public A a;
But it gives me the error:
Unable to find column with logical name in table A
It appears that the "name" property of the column it has to find is blank someway.
Need some help please!
Please have a look at Official Java EE 6 Tutorial about composite primary key. You can use #EmbeddedId #Embeddable and/or #IdClass annotations.
For example
// File: APK.java ---------------------
#Embeddable
public class APK implements Serializable {
public Integer idShared;
public String nKey;
}
// File: A.java ---------------------
#Entity
public class A {
#EmbeddedId public APK id;
}

Hibernate create foreign key to itself

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.

Categories

Resources