Java custom entity relation - java

I have an Entity with some default fields. But I need to have ability to extend it with dynamic fields as well. That fields are related to a table (not to specific entity). I need ability to add them from admin panel (it is some sort of EAV).
So I have Entity like this
public class Account {
#Id
private Long id;
#Column(name = "account_name")
private String accountName;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
//getters setters
}
For example, I want to extend it with new column 'salutation'.
I've created 2 tables: first for custom field names, second for appropriate values:
CREATE TABLE custom_fields
(
id BIGINT(20) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
entity VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL
);
CREATE TABLE custom_field_values
(
id BIGINT(20) PRIMARY KEY NOT NULL AUTO_INCREMENT,
custom_field_id BIGINT(20) unsigned NOT NULL,
entity_id BIGINT(20) unsigned NOT NULL,
value VARCHAR(255) NOT NULL
);
CREATE UNIQUE INDEX UQ_custom_value ON custom_field_values(custom_field_id, entity_id);
Now I need some relation on Account entity, to obtain Map of customFieldName=>customFieldValue. Is it possible to create relation only by where clause, like
SELECT name FROM custom_fields WHERE entity = "account"
for map key, and for values
SELECT value FROM custom_field_values INNER JOIN custom_fields ON custom_fields.id = custom_field_values.custom_field_id WHERE entity_id = :id
Is it possible? I need to provide ability to create this columns from admin panel, and ability to fill previously created custom fields for users.
Thanks!

Related

Unique key auto generated in Spring Boot Entity

I have a primary key in my entity table which is autogenerated but now I want unique keys to be auto generated so how to do it
Please help me out.
#Entity
#Table(name = "director")
public class Director {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
//how to make this field auto generated like above one
#Column(name = "subid", unique=true)
private long sub_id;
My database table picture is here please refer
You can use timestamp or static AtomicLong counter as sub_id value. Try to define method with annotation #PrePersist in your entity class and your JPA provider will execute it before persisting an object.
Note: using timestamp in concurrent environment may lead to collisions and values won't be unique.
private final static AtomicLong subIdCounter = new AtomicLong(System.nanoTime());
#PrePersist
void sub_id() {
this.sub_id = subIdCounter.incrementAndGet();
}
After a short study it seems that that Hibernate supports the feature of generated values only with fields annotated with #Id. With #Id and default #generatedValue Hibernate creates - depending on the database and dialect used - appropriate way to generate the value of id field. usually this is something like creating a sequence and setting the column definition like (examples are from Postgres 12):
id bigint not null nextval('director_id_seq'::regclass)
Interesting thing is that this is done by issuing create statement like this:
create table director (id bigserial not null, primary key (id))
So, the column type bigserial actually generates sequence that is used to insert default value to the id column.
There are two options it you want to generate the value for column sub_id as it is generated to the column id. Both are database dependent.
Just create the sequence manually to the database and alter column sub_id to fetch the default value from the sequence.
OR
Change your column definition to use appropriate column type, like:
#Column(name = "subid", insertable = false,
nullable = false, unique = true, columnDefinition = "bigserial")
private long sub_id;
This will cause Hibernate to generate table like:
create table director (id bigserial not null, subid bigserial not null, primary key (id))
and result to a column like:
subid bigint not null nextval('director_subid_seq'::regclass)
But again: this is database specific stuff.
Also note: that JPA is aware only of the value that is stored to the id field. The subid is inserted to the database table but the sub_id field is not populated until entity is refreshed in its persistence context.

JPA/Hibernate: Update Parent column value before inserting child column value

This is my scenario. I have a Parent table Files_Info and a child table Files_Versions.
create table files_info(
id bigint primary key,
name varchar(255) not null,
description varchar(255) not null,
last_modified TIMESTAMP,
latest_version integer default 0 not null
);
create table files_versions(
id bigint primary key,
file_id bigint references files_info(id),
version integer not null,
location text not null,
created TIMESTAMP,
unique(file_id, version)
);
This is mainly to track a file and its various versions. When the user initiates a new file creation (not yet uploaded any version of the file), an entry is made to the files_info table with basic info like name, description. The latest_version will be 0 initially.
Then when the user uploads the first version, an entry is created in the files_versions table for that file_id and the version
value is set as parent's latest_version + 1. Parent's latest_version is now set to 1.
The user can also upload an initial version of the file when he/she initiates a new file creation. In that case, parent record
will be created with latest_version as 1 and also the corresponding version 1 child record.
I do not know how to design this using JPA / Hibernate.
I wrote my Entity and Repository classes and the save methods seem to work independently. But I do not know how to do the simultaneously latest_version updates.
Can this be done using JPA / Hibernate? Or should it be a database trigger?
A trigger is a valid option, but It can be done using JPA/Hibernate.
I'll suggest to use #PrePersist annotation on some method defined at the files_versions entity ... This method will be called by JPA when you execute: EntityManager.persist(FileVersion); and it can be use to update entity's derivative attributes ... In your case, will be the sum of the file last_version + 1 ... Example:
#Entity
#Table(name = "files_info")
public class FileInfo {
}
#Entity
#Table(name = files_versions)
public class FileVersion {
... //some attributes
#Column(name = "version")
private int version;
#ManyToOne
#JoinColumn(name = "file_id")
private FileInfo fileInfo;
... //some getters and setters
#PrePersist
private void setupVersion() {
// fileInfo should be set before of calling persist()!
// fileInfo should increase its lastest Version before of calling persist()!
this.version = this.fileInfo.getLastVersion();
}
}

How to implement "is–a" relationship when parent and child have separate Ids

I have 2 tables: person and user. Each user is a person. Each person is not a user.
Each table has its own PK. This is the DDL of the tables:
CREATE TABLE person (
person_pk int NOT NULL PRIMARY KEY,
first_name varchar(50) NOT NULL,
middle_name varchar(50) NULL,
last_name varchar(50) NOT NULL
);
CREATE TABLE user (
user_pk int NOT NULL PRIMARY KEY,
user_name varchar(50) NOT NULL UNIQUE,
password varchar(255) NOT NULL,
person_fk int NOT NULL FOREIGN KEY REFERENCES person(person_pk)
);
Now I want to define the "is–a" relationship of person and user in OOP. I thought it would be straightforward by defining 2 classes: Person class and User Class this way:
public class Person {
...
}
public class User extends Person {
...
}
But I am confused about how to handle Id in this case. I thought that in "is–a" relationship there is only 1 Id which is defined in Person class. But in this case each table has its own Id. How can I implement "is–a" relationship in this case?
The data mode is not defined as an "is-a" relationship, it's really a 1-to-many where 1 person can be associated with multiple users. True, you can enforce via business rules a 1-to-1 relationship but it's really not.
As it stands, you truly have two different IDs which you were hoping to treat as a single entity, but really can't be. The best I can come up with is to put the attributes in the classes just as they're mapped via data, and when you're dealing with the person you'll access the ID in the parent class/table and when you're dealing with the user ID in the child class/table.
However, by no means is this a clean design.
One way to do what you describe is to redefine your tables this way:
CREATE TABLE person (
id int NOT NULL PRIMARY KEY,
first_name varchar(50) NOT NULL,
middle_name varchar(50) NULL,
last_name varchar(50) NOT NULL
);
CREATE TABLE user (
user_name varchar(50) PRIMARY KEY,
password varchar(255) NOT NULL,
person_fk int NOT NULL UNIQUE,
FOREIGN KEY (person_fk) REFERENCES person(id)
);
the defined PK in the table user is totally unnecesary, as you have a user_name which is already NOT NULL and UNIQUE, which is the basic definition of a primary key.
Also defining person_fk as NOT NULL and UNIQUE, you are establishing a 1 to 1 relationship between user and person, so this way you can use the primary key of person to identify a record in table user.
This way you can implement your classes like this:
public class Person {
protected final int id;
}
public class User extends Person {
...
public int getID() { return this.id }
}

Hibernate get one column from a one to many connection

I want to create a java class that contains only 1 column from OneToMany ManyToOne etc. type connection not the whole row.
How can I do that?
(I'm not sure that I could express myself so I made an example)
TABLE e_skill
(
id int NOT NULL AUTO_INCREMENT,
skill_name VARCHAR (20) NOT NULL,
PRIMARY KEY (id)
);
TABLE t_person
(
id int NOT NULL AUTO_INCREMENT,
user_id int NOT NULL,
primary_skill int,
PRIMARY KEY (id),
FOREIGN KEY (primary_skill) REFERENCES e_skill(id)
);
TABLE t_secondaryskills
(
id int NOT NULL AUTO_INCREMENT,
t_person_id int NOT NULL,
skill_name int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (t_person_id) REFERENCES t_person(id),
FOREIGN KEY (skill_name) REFERENCES e_skill(id)
);
public enum Skill {
...
}
#Entity
#Table(name = "t_person")
public class Employee {
#Id
#GeneratedValue
private Integer id;
#ManyToOne
#Enumerated(EnumType.STRING)
//????????
//get skill_name column from e_skill
//????????
private Skill primarySkill;
#OneToMany
#Enumerated(EnumType.STRING)
//????????
//get skill_name column from e_skill
//????????
private Set<Skill> secondarySkills;
//getters setters
}
The only way I could do it now is to create a Entity to represent the e_skill table, I want to avoid that, because I only need 1 column from it.
If I understand your question correctly, you can't do what you want because of the secondary skills (because it's a collection). You can only map the primary skill name though using the #SecondaryTable annotation.
When you map things using an ORM there's no such thing as I only want a column in this scenario as you're mapping Objects, and usually in your objects you don't want to replicate data (unless they are outside your domain model). If this is unacceptable for you, I suggest you to take a look at other tools like myBtais, which gives you full control on the data you get back.
So bottom line, map your skill as an entity and live with it even if it has many columns, or choose a different tool (but not an ORM).

Null or Zero Primary Key in unit work clone sqlite

Hello I have a Problem with JPA and SQLite.
I have a table called Category in SQLite. This is the Structur of this table:
Category:
catID INTEGER Primary Key
catName TEXT
Now i'm trying to insert a new Category via JPA.
This is my method to do this:
public void insertCategory(EntityManager em, String catName) {
Category cat = new Category();
cat.setCatName(catName);
em.getTransaction().begin();
em.persist(cat);
em.flush();
em.getTransaction().commit();
}
Now I have an Execption at em.flush().
The Exception says
Null or zero primary key encountered in UnitOfWork clone
I know this happens because of the Object cat. The catID is still null.
The class looks like:
#Entity
#Table(name="Category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="catName")
private String catName;
#Id
#Column(name="catID")
private Integer catID;
+ Getter and Setter
}
SQLite increments the Primary Key automatically when not given in the query. How can i inform my Program about the Primary Key to run without getting this Exception??
Thank you Mick Mnemonic for your help. The last answer you give helped me.
I had to generate my database new and add the autoincrement Argument to my Primary Keys. Then the sqlite_sequence Table was generated. Now i can insert Entitys in my database via JPA without getting any Exception.
This is my Generator Annotation:
#GeneratedValue(generator="sqlite")
#TableGenerator(name="sqlite", table="sqlite_sequence",
pkColumnName = "name", valueColumnName = "seq",
pkColumnValue = "Section", initialValue = 1,
allocationSize = 1)
the last two Arguments are necessary because jpa seems to add +50 to the value in the sqlite_seq table and SQLite begins the Autoincrement value at 1.

Categories