I have a problem regarding my implementation of a weak entity type. (Note, if that's important: I'm using H2 as my database)
Here is my datamodel:
I'm trying to implement Activity's concatenated primary key using two #ID annotated columns. Here is my activity class as well as its "owner"-class:
#Entity
public class Activity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id
#ManyToOne
#JoinColumn
private UserGroup group;
...
}
#Entity
public class UserGroup {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
My expected outcome would be, that inside my activity table id is generated by default as identity and group_id is set depending on the corresponding group-join. Basically something like this:
create table activity
(
id bigint generated by default as identity,
begin_date_time timestamp(6),
end_date_time timestamp(6),
name varchar(255) not null,
note varchar(255),
group_id bigint not null,
room_id bigint,
primary key (group_id, id)
);
Instead, spring disregards my #GeneratedValue annotation and sets it to group_id, ignoring my actual activity.id column:
create table activity
(
id bigint not null,
begin_date_time timestamp(6),
end_date_time timestamp(6),
name varchar(255) not null,
note varchar(255),
group_id bigint generated by default as identity,
room_id bigint,
primary key (group_id, id)
);
This is a problem, as I plan on using Rest-Endpoints and Repositories to run CRUD operations using HTTP-requests. Having an ID-counter that increments and gets new IDs for one table only seems unnecessary and dirty.
Can somebody explain to me why this is happening? Can I manually annotate group_id as a manually assigned field as a quick workaround?
Cheers!
What you are looking for is a composite key, which you can create with either #IdClass or with #Embeddable and #EmbeddedId. The catch is, however, that neither of these approaches will work with a #GeneratedId.
If you think about it, there's no need to have a composite key where one of the parts is autogenerated. Just have one Id on your entity that's auto generated, and if you want, create an index across (group_id, id).
Related
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.
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.
I have such hibernate mapping annotation and I need to translate it to xml one.
#Entity
#Table(name = "nodes")
public class DefaultDiagramNode {
.....
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "node_id", referencedColumnName = "id")
private Set<NodeProperty> properties;
}
Here is my sql table structure:
CREATE TABLE nodes (
id VARCHAR(255) NOT NULL,
logical_id VARCHAR(255) NOT NULL,
graphical_id VARCHAR(50) NOT NULL,
type VARCHAR(50) NOT NULL,
diagram_id BIGINT,
PRIMARY KEY (id),
FOREIGN KEY (diagram_id) REFERENCES diagrams (diagram_id)
);
CREATE TABLE node_properties (
property_id VARCHAR(255) NOT NULL,
name VARCHAR(50) NOT NULL,
value VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
node_id VARCHAR(255),
PRIMARY KEY (property_id),
FOREIGN KEY (node_id) REFERENCES nodes (id)
);
How can I do it?
Annotations really are a better choice for a new application. They are easier to read, remove the obtuse relationship between POJO and XML, and they less will result in less lines of code to maintain later. The annotations will also make it far easier to refactor your applications using IDEs such as Eclipse or IntelliJ.
Is there a pressing need to go back to XML? Otherwise, I'd avoid it.
Edit:
Ah, makes sense since you're using Thrift. Have have you tried looking at Swift? You may be able to just add these annotations to your Hibernate entity classes. https://github.com/facebook/swift/
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
#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