JPA Entities Generating Wrong Database Design - java

In my project, an Admin (User) can set to receive scheduled emails about any User he chooses.
I need to have a database of the following design:
TABLE User (
UserId INT PRIMARY KEY AUTO_INCREMENT,
Email VARCHAR,
FirstName VARCHAR,
LastName VARCHAR
IsAdmin BOOL,
...
)
TABLE Email_Schedule (
ScheduleId INT PRIMARY KEY AUTO_INCREMENT, /* this is not necessary */
AdminId INT, /* could be replaced by a composite foreign primary keys */
UserId INT,
FOREIGN KEY (AdminId) REFERENCES User (UserId),
FOREIGN KEY (UserId) REFERENCES User (UserId)
)
The following code in my Java classes for JPA entity:
#Entity
public class Email_Schedule {
#Id
private int scheduleId;
#ManyToOne(targetEntity = User.class)
private List<User> admins = new LinkedList<>();
#ManyToOne(targetEntity = User.class)
private List<User> users = new LinkedList<>();
public Email_Schedule() {
super();
}
public Email_Schedule(User admin, User user) {
super();
this.admins.add(admin);
this.users.add(user);
}
// setters and getters...
generates a database of the following schema:
TABLE USER (
...
)
TABLE SCHEDULE (
ScheduleId INT PRIMARY KEY AUTO_INCREMENT
)
TABLE Email_Schedule (
ScheduleId INT,
Users INT,
Admins INT,
FOREIGN KEY (ScheduleId) REFERENCES SCHEDULE(ScheduleId),
FOREIGN KEY (Users) REFERENCES USER (UserId),
FOREIGN KEY (Admins) REFERENCES USER (UserId)
)
My question is why did it create a useless table for ScheduleId and referenced it from another table instead of just using it directly in Email_Schedule table?
The problem seems to be with the ScheduleId.. I tried not to use it by creating IdClass but I got different errors and wrong database designs.

EclipseLink is using TABLE for generating sequence for scheduleId.
This seems to be the default.
You can use a table for identifier generation on any database. This
strategy is completely portable across databases and will be
automatically generated for you when schema generation is enabled.
As per EclipseLink Documentation, you may have to use generation strategy of IDENTITY for scheduleId to avoid the TABLE appraoch.
#GeneratedValue(strategy=GenerationType.IDENTITY)
Note that if you use AUTO strategy as shown below, then, even in that case, EclipseLink may pick TABLE strategy for ID generation.
#GeneratedValue(strategy=GenerationType.AUTO)
Using a Default Generation Strategy
Specifying a strategy of AUTO
allows EclipseLink to select the strategy to use. Typically,
EclipseLink picks TABLE as the strategy, since it is the most portable
strategy. However, when AUTO is specified, schema generation must be
used at least once in order for the default table to be created in the
database.
More details here at PrimaryKey and GeneratedValue Documentation

Related

Create an Entity Class with two foreign keys - Spring JPA

How can i create entity class for the below table which has two foreign keys of two different tables.
CREATE TABLE `flights_info` (
`airline_id` bigint(20) NOT NULL,
`flight_infoid` bigint(20) NOT NULL,
UNIQUE INDEX `UK_mnghyk14c0ufcb2gs2k6fab40`(`flight_infoid`) ,
INDEX `FKm5m2579nqtr1wele0bimvme8m`(`airline_id`) ,
CONSTRAINT `FKlda61sltnw69kxw7b0gx6sj5s` FOREIGN KEY (`flight_infoid`) REFERENCES `flight_info` (`flight_infoid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FKm5m2579nqtr1wele0bimvme8m` FOREIGN KEY (`airline_id`) REFERENCES `airline_info` (`airline_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
);
my entity class:
#Entity
public class FlightsInfo {
#Id
#JoinTable(name="AirlineInfo", joinColumns=#JoinColumn(name="airline_id"))
private AirlineInfo airline_id;
#OneToOne
#JoinColumn(name="flight_infoid")
private FlightInfo flight_infoid;
}
The problem is that your table does not have a primary key. So it's hard to point the #Id annotation at the right column. JPA however accepts tables without PKs as long as you have a unique column: https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key
Luckily you have a unique constraint on the flight_infoid column, so there you should try to point your #Id annotation.

Schema-validation: missing table [...] error message using flyway for a SQL code generated by Hibernate

I've been trying to deal with some problems regarding Flyway. My situation is the following: I have two Java classes, which I'd like to migrate as two schemas. Let's name them Table and CustomTable. My java classes look like:
#Entity
public class xtable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//getters, setters, constructors
#Entity
public class CustomTable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String a;
private String b;
private String c;
//getters, setters, constructors
My application.properties:
spring.flyway.url=${env.var1}
spring.flyway.user=${env.var2}
spring.flyway.password=${env.var3}
spring.jpa.hibernate.ddl-auto=validate
//If I use create-drop, hibernate creates it, but after that the validation fails
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.logging.level.org.hibernate.SQL=debug
spring.jpa.show-sql=true
hibernate.temp.use_jdbc_metadata_defaults=true
spring.flyway.enabled=true
My build.gradle:
plugins {
id "org.flywaydb.flyway" version "5.2.4"
}
dependencies {
implementation 'org.flywaydb:flyway-core'
}
The situation is so weird, because it does not even work with the auto-generated SQL code, which I let the program create without flyway.
It looks like this:
create table custom_table (
id bigint not null,
a varchar(255),
b varchar(255),
c varchar(255),
xtable_id bigint,
primary key (id)
)
engine = InnoDB;
create table xtable (
id bigint not null,
name varchar(255),
xtable_id bigint,
primary key (id)
)
engine = InnoDB;
alter table custom_table
add constraint FKep6vooglihwraille12muox9 foreign key (xtable_id) references xtable (id);
alter table xtable
add constraint FK426q765pr4gv5wux6jaktafqk foreign key (custom_table_id) references custom_table (id);
I also don't understand why Hibernate creates one-one foreign key into each class, but the bigger problem is that I still get the error message
Schema-validation: missing table [custom_table]
I tried renaming custom_table to customtable (and also renaming the class in Java), but the error message was the same.
Have you ever met the same problem? Have you got any suggestions? I've been working on this problem for - at least - 2 days.
I looked for relevant - or seemingly identical - topics here, but I couldn't find a good solution.
Thank you.
Finally I got the problem. The problem was with inserting multiple foreign keys. (So these two lines):
alter table custom_table
add constraint FKep6vooglihwraille12muox9 foreign key (xtable_id) references xtable (id);
alter table xtable
add constraint FK426q765pr4gv5wux6jaktafqk foreign key (custom_table_id) references custom_table (id);
I couldn't figure out, though, the reason why Flyway couldn't handle this, but when I recreated the whole structure with the two tables and another one containing the proper ID's, doing exactly the same thing in the whole project, it worked.

Mapping Entity objects by database schema in intelij generates bad annotations and relations

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.

Hibernate not preserving referential integrity

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.

Why does not jpa2/eclipselink generate on delete cascade SQL?

#Entity
public class MUser implements Serializable, MemoEntity {
private static final long serialVersionUID = 1L;
#Id
private String email;
#OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Meaning> mengs = new HashSet<Meaning>();
Shouldn't this mean that I get the constraint with a "on delete cascade"?
This is what gets generated instead:
CREATE TABLE MUSER_MEANING (MUser_EMAIL VARCHAR(255) NOT NULL, mengs_OBJID INTEGER NOT NULL, PRIMARY KEY (MUser_EMAIL, mengs_OBJID))
CREATE TABLE MUSER_MEANING (MUser_EMAIL VARCHAR(255) NOT NULL, mengs_OBJID INTEGER NOT NULL, PRIMARY KEY (MUser_EMAIL, mengs_OBJID))
ALTER TABLE MEANING ADD CONSTRAINT MEANING_USR_EMAIL FOREIGN KEY (USR_EMAIL) REFERENCES MUSER (EMAIL)
ALTER TABLE MUSER_MEANING ADD CONSTRAINT MSRMEANINGMsrEMAIL FOREIGN KEY (MUser_EMAIL) REFERENCES MUSER (EMAIL)
I'm trying to make it as such that deleting a MUser deletes all Meanings associated to it.
Why does not jpa2/eclipselink generate on delete cascade SQL?
Because that's not how things work. Specifying a cascade=REMOVE means that the remove operation will be cascaded to entities by the JPA provider, not at the database level using a constraint.
Some providers do have extensions to use a cascade delete constraint at the database level instead of the regular mechanism. For example with Hibernate, you can specify:
#OnDelete(action=OnDeleteAction.CASCADE) on joined subclasses: use a SQL cascade delete on deletion instead of the regular Hibernate mechanism.
But I don't know if EclipseLink has something equivalent.
References
JPA 2.0 Specification
Section 3.2.3 "Removal"
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions

Categories

Resources