Hibernate not persisting foreign keys in Mysql - java

I have an entity Property which has city field. Something like this:
#Entity
class Property {
...
#ManyToOne(fetch = FetchType.LAZY)
private City city;
...
}
So each property (e.g house or apartment) belongs to only one city.
Now here is a thing, if I try to log generated SQL by Hibernate, it is generating the foreign key in a right way:
Hibernate:
alter table property
add constraint FKdn1hnohufcwdr4a401xabcjn
foreign key (city_id_city)
references city (id_city)
However, if I check my Mysql database, there is no foreign key there, only index is generated:
show create table property;
leads to:
| property | CREATE TABLE `property` (
`id_property` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`deal_type` varchar(15) NOT NULL,
`publisher_type` varchar(15) NOT NULL,
`type` varchar(15) NOT NULL,
`updated_at` datetime NOT NULL,
`city_id_city` int(11) DEFAULT NULL,
PRIMARY KEY (`id_property`),
KEY `FKdn1hnohufcwdr4a401xabcjn` (`city_id_city`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 |
So the question is why there is no CONSTRAINT FOREIGN KEY () REFERENCES definition in my database?

The problem ended up to be the database engine after I switched to InnoDB engine it started working, as MyISAM does not implement foreign keys.
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

The annotation #ManyToOne is used to map two tables that have a relationship instance by a foreign key, it could not automatically create a foreign key in your database

You need to define the list Property in the City Entity and add the relationship there as well

Related

How to exclude many-to-one relationship using hibernate reverse engineering?

I've two tables user and patient. Patient have a user id in it i.e user_id F.K to user.id.
When I create a model classes using reverse engineering, in User model it create Set but in that I need only Patient. My business model says one-to-one relationship between user & patient.
so is there a way to do it ?
Below is table schema.
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Unique_Email_Id` (`user_email`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `patient` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `PATIENT_EMAIL_ID_UNIQUE_ID` (`email_id`),
UNIQUE KEY `UNIQUE_USER_ID` (`user_id`),
CONSTRAINT `User_Id_Foreign_Key_To_Patient_Id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
hibernate.reveng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >
<hibernate-reverse-engineering>
<type-mapping>
<sql-type jdbc-type="BIT" hibernate-type="int"></sql-type>
</type-mapping>
<table-filter match-name="user">
<table-filter match-name="patient"></table-filter>
<table name="patient">
<foreign-key constraint-name="Unique_user_Id">
<many-to-one exclude="true"/>
</foreign-key>
</table>
</hibernate-reverse-engineering>
I have solved the problem using a personalized strategy. For this we have to follow the following steps:
1-New dependence on maven:
<!-- Hibernate tools para estrategia presonalizada -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-tools</artifactId>
<version>5.3.7.Final</version>
</dependency>
2-Create custom strategy class (CustomReverseEngineeringStrategy) extending from DelegatingReverseEngineeringStrategy.
package com.xxx.model.strategy;
import java.util.List;
import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.TableIdentifier;
public class CustomReverseEngineeringStrategy extends DelegatingReverseEngineeringStrategy {
...
}
3-Overwrite excludeForeignKeyAsManytoOne method, where we will set a criteria based on which we will indicate which manytoone relationships must be taken into account and which ones are not:
#Override
public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List<?> fromColumns, TableIdentifier referencedTable, List<?> referencedColumns) {
return referencedTable.getName().startsWith(EXCLUDED_REFERED_TABLE_PREFIX);
}
4-Adapt the configuration 'hibernate code generation' to make use of the customized strategy:
Unfortunately, the exclude annotation does not seem to work.
I hope this solution is useful. Using the personalized strategy can achieve many things because we can overwrite multiple methods that control almost all aspects of the reverse generation of entities.

Hibernate #ManyToOne mapping error

Good day to you. I have 2 tables.
CREATE TABLE A(
id NUMBER(10) NOT NULL,
name VARCHAR2(255) NULL,
category VARCHAR2(255) NULL,
is_hidder NUMBER(1) NULL,
is_enabled NUMBER(1) NULL,
b_id NUMBER(10) NULL,
CONSTRAINT pk_a PRIMARY KEY (ID),
CONSTRAINT fk_to_b
FOREIGN KEY (vj_users_preferences_id)
REFERENCES VJ_USER_PREFERENCES(ID)
);
CREATE TABLE B(
ID NUMBER(10) NOT NULL,
EXT_ID NUMBER(10) NOT NULL,
KEY VARCHAR2(1024) NOT NULL,
VALUE VARCHAR2(1024) NOT NULL,
CONSTRAINT PK_B PRIMARY KEY(ID)
);
For both tables I have entities and I want to map them #OneToOne.
class Entity A{
.......
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="pk_b", referencedColumnName = "id")
private B b;
}
Entity B is Audited and haven't got A field so connection is unidirectional. When I try to persist the A entity I got the error:
Caused By: org.hibernate.HibernateException: Could not determine type of dynamic map entity
This exception is being thrown from:
org.hibernate.tuple.entity.DynamicMapEntityTuplizer.BasicEntityNameResolver#resolveEntityName
Because the input is Map and it has not got: DynamicMapInstantiator.KEY in it.
Can somebody help me with this issue? I cannot figure out why Hibernate is creating that map without necessary value. Is there something wrong with my mapping?
It appeared that my UI form was receiving over 50 symbols, but database had:
EXT_ID NUMBER(10) NOT NULL
When I altered table to reflect UI possibilities (e.g. EXT_ID NUMBER(50) NOT NULL) then error was gone.
That exception, as per my understanding, is misleading in this kind of situation. I was expecting to see: SQLGrammarException of kind of it.
Hope this will help oneday.

Replace row when duplicate primarykey found

I have a table where I wish to replace a row when duplicate Primary (unique) key is found.
Create table History (
id varchar(5) not null,
name varcah(30),
primary key (id)
) engine=InnoDB character set utf8;
I'm using this with hibernate. id column is declared as #Id #Column(name="id", unique=true, nullable=false)
Help me change the above SQL, Hibernate annotations to allow REPLACE on duplicate primary key is found
The database Primary Key is not meant to be ever updated/replaced. There are workarounds but those are bad-practices.
You'd better use AUTO INCREMENT(MySQL), IDENTITY(MSSQL) or SEQUENCE(ORACLE, PostgreSQL) ID generation.
If you use MANUAL ID assignment and you get duplicate primary key violations, you have to check your current application concurrency design. Is the manual id assignment not thread-safe?
A database sequence or an AUTO-INCREMENT ID will save you from getting duplicate primary key violations.

How to decouple database entity dependency in SOA?

I have two services: TeacherService and PupilSerivce which are sharing the same database. The relationship between them is one-to-many that says one teacher can have many pupils and each pupil only has one teacher.
CREATE TABLE `test`.`teacher` {
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(40),
PRIMARY KEY (`id`)
} ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `test`.`pupil` {
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`teacher_id` bigint unsigned NOT NULL COMMENT 'teacher id',
PRIMARY KEY (`id`),
CONSTRAINT `fk_teacher_id` FOREIGN KEY (`teacher_id`) REFERENCES `teacher`(`id`) ON DELETE CASCADE
} ENGINE=InnoDB DEFAULT CHARSET=utf8;
as you can imagine, I have two entities TeacherVO and PupilVO that represents database table in my Java code.
the question is, both TeacherService and PupilSerivce run in individual process and communicate with messages, I don't want they have any compilation dependency on each other. However, when adding a new pupile the method looks like:
PupilService.addNewPupil(long teacherId) {
if (isValidTeacher(teacherId) {
// add the pupile
}
}
this requires PupileService has knowledge of TeacherVO so it can do some validation, but TeacherVO is in TeacherService package!
What's the best practice to remove such sort of dependency?
There are some ways I thought of:
create a validateTeacher message, then PupilService sends this message to TeacherService and wait for response. However, if I have further requirements like searching a teacher with name, then I have to create another message which at last results in message blowing up. Directly searching database is a more flexible and efficient way, but it introduces dependency.
don't do any check and catch SQL exception caused by foreign key if the teacher_id is invalid. however, this can not solve the problem that I may have further requirements.
I think this should be a common issue in SOA architecture in case of multiple services share the same database. I did research a while but not get anything valuable.
I agree with Tony Hopkinson, what you're trying to do is fundamentally unsound. You cannot separate the concerns of the teacher and student.

Spring Roo referencedColumnNames not mapped to a single property

I am trying Roo database reverse engineering and I had a problem with my first real database. For this question, I created a minimal example showing the problem. In the DB there is a sl_person table a sl_group table and a bridge table sl_person_group, since a person can belong to 0 or more groups.
CREATE DATABASE `rooperson` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE IF NOT EXISTS `sl_group` (
`id_group` int(11) NOT NULL DEFAULT '0',
`name` varchar(80) NOT NULL,
`description` text,
UNIQUE KEY `id_group_idx` (`id_group`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `sl_person` (
`id_person` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
`surname` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id_person`),
KEY `name` (`name`),
KEY `surname` (`surname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5727 ;
CREATE TABLE IF NOT EXISTS `sl_person_group` (
`id_person` int(11) NOT NULL DEFAULT '0',
`id_group` int(11) NOT NULL DEFAULT '0',
UNIQUE KEY `id_person_group_idx` (`id_person`,`id_group`),
KEY `id_group` (`id_group`)
) ENGINE=InnoDB DEFAULT CHARSET;
ALTER TABLE `sl_person_group`
ADD CONSTRAINT `sl_person_group_ibfk_2` FOREIGN KEY (`id_group`) REFERENCES `sl_group` (`id_group`),
ADD CONSTRAINT `sl_person_group_ibfk_1` FOREIGN KEY (`id_person`) REFERENCES `sl_person` (`id_person`);
In Eclipse I create a new Spring Roo project with Spring Source Tool Suite: File -> New -> Project -> Spring Roo Project
Project Name: rooperson
Top level package name: org.obliquid.rooperson
Next -> Finish
Then in roo shell I type:
persistence setup --database MYSQL --provider HIBERNATE
database properties set --key database.password --value xxxxx
database properties set --key database.username --value rooperson
database properties set –key database.url –value jdbc:mysql://localhost/rooperson?zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8
database reverse engineer --schema PUBLIC --package org.obliquid.rooperson.domain
web mvc setup
Then I fixed a small problem with stylesheets.
However, when I try to start GlasshFish 3.1.1, the application doesn't start with the following error.
INFO: 2011-09-11 20:42:59,562 [admin-thread-pool-4848(3)] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager'
defined in file [/usr/local/glassfish3/glassfish/domains/domain1/eclipseApps/rooperson/WEB-INF/classes/META-INF/spring/applicationContext.xml]:
Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory';
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'entityManagerFactory' defined in file
[/usr/local/glassfish3/glassfish/domains/domain1/eclipseApps/rooperson/WEB-INF/classes/META-INF/spring/applicationContext.xml]:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: referencedColumnNames(id_group)
of org.obliquid.rooperson.domain.SlPersonGroup.idGroup referencing org.obliquid.rooperson.domain.SlGroup not mapped to a single property
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
The generated file SlPersonGroup_Roo_DbManaged.aj starts with:
privileged aspect SlPersonGroup_Roo_DbManaged {
#ManyToOne
#JoinColumn(name = "id_group", referencedColumnName = "id_group", nullable = false, insertable = false, updatable = false)
private SlGroup SlPersonGroup.idGroup;
#ManyToOne
#JoinColumn(name = "id_person", referencedColumnName = "id_person", nullable = false, insertable = false, updatable = false)
private SlPerson SlPersonGroup.idPerson;
How can I solve the problem? If I should provide more information let me know.
Similar problem here. It seems this version of Roo has a problem with composite primary keys (as in your sl_person_group table).
While making the key refer to a single column will stop this from happening, unfortunately it's probably not what your data model calls for.

Categories