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

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.

Related

Hibernate not persisting foreign keys in Mysql

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

Use join into hibernate mapping against main class foreing key

This is a simple description of my situation and the requirement I am not able to implement:
I have two tables "address" and "person" related with a FK:
CREATE TABLE `address` (
`id_address` int(11) NOT NULL,
`street` varchar(250) DEFAULT NULL,
`city` varchar(250) DEFAULT NULL,
PRIMARY KEY (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `person` (
`id_person` int(11) NOT NULL,
`name` varchar(250) DEFAULT NULL,
`id_sent_address` INT(11) NOT NULL,
PRIMARY KEY (`id_person`),
CONSTRAINT `fkSentAddress` FOREIGN KEY (`id_sent_address`) REFERENCES `address` (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I want to create a hibernate mapping using this two tables to create a simple model.
It is important the fact that I NEED to do the mapping using "join" having something like this:
<class name="xxx.PersonAndAddress" table="person">
<id name="idPerson" type="java.lang.Integer">
<column name="id_person" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String" column="name"/>
<join table="address">
<key column="id_address" />
<property name="street" column="street" />
<property name="city" column="city" />
</join>
</class>
This is not working because the "join" key column "id_address" is creating the mapping against the primary key "id_person", and I don't know how can I change this behaviour column to use the person's "id_sent_address" instead "id_person". Is there any solution?
As I said, this is a simple situation that describes my real problem, which involves more fields and more logic, but this is the essence of the issue.
Right now I'm using formulas for each address's field but it is a big quantity of fields and I would use join to improve its performance.
Thanks a lot!
Two things, first of all, ORDER MATTERS. You need to create your address table first, before you can create your person table, as person table's id_sent_address DEPENDS on the id_address from address table.
Secondly, to set up a foreign key, the target key MUST be a key as well, which you forgot to add.
So once the two errors are fixed like below, you should be able to execute it without errors.
CREATE TABLE `address` (
`id_address` int(11) NOT NULL,
`street` varchar(250) DEFAULT NULL,
`city` varchar(250) DEFAULT NULL,
PRIMARY KEY(`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `person` (
`id_person` int(11) NOT NULL,
`name` varchar(250) DEFAULT NULL,
`id_sent_address` INT(11) NOT NULL,
PRIMARY KEY (`id_person`),
CONSTRAINT `fkSentAddress` FOREIGN KEY (`id_sent_address`) REFERENCES `address` (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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