I'm working on a JAVA project and need to insert an entity in my DB using Hibernate but the fields need to be inserted only once.
I have this ProjectEntity class that I need to insert to my database :
#Entity
#Setter
#Getter
public class ProjectEntity extends PanacheEntity {
private String projectname;
private String projectkey;
}
My projectentity table looks like this :
CREATE TABLE projectentity
(
id BIGINT NOT NULL
CONSTRAINT project_pkey
PRIMARY KEY,
projectname TEXT,
projectkey TEXT,
);
I tried to put the fields in the table as unique but obviously it is not enought as I get an error when I'm trying to insert the same fields and it blocks the execution of my program.
I want to know how can I manage the insertion of the project entity once when a new project is specified and then not do it again if the project already exists in my database.
You can use #Column for every fields and set unique=true .
example :
#Column (name "projectname",nullable = false, unique = true )
private String projectname;
Related
I have an entity class which uses auto generated id from database (PostgreSQL). It has been persisting fine without requiring me to specify an id to it. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ... other columns
}
Now I want to add a List of associated entities owned by this entity class with uni-directional association. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL) #JoinColumn(name="pid")
private List<SubEntity> subEntities;
// ... other columns
}
#Entity public class SubEntity implements Serializable {
#Id private Integer pid; // refer to id of MyEntity
#Id private String name; // pid, name forms a composite key for SubEntity
// ... other columns
}
Then I bumped into an issue that JPA (Hibernate in this case) was generating SQLs like:
INSERT INTO MYENTITY (...) VALUES (...)
INSERT INTO SUBENTITY (pid, ...) VALUES (null, ...)
It failed when trying to insert a null value to pid as it has not null constraint in the database schema. If I bypass this, Hibernate then generates an update statement to update the null value with the generated id from MyEntity:
UPDATE SUBENTITY SET pid = ? WHERE pid = null AND name = ?
I get that the auto generated id is not known until after the insert to MyEntity, so it updates afterward. But I wonder if there is a solution so that Hibernate does the insert to MyEntity ONLY first, get the generated id THEN does the inserts to SubEntity with the correct pid and no update afterward?
This should be possible. Please create an issue in the Hibernate issue tracker with a test case that reproduces this issue. Apart from that, I would suggest you try using a sequence generator as that is more scalable anyway.
Now I have two tables, the first table called StudentBase and has three columns: id, firstname and lastname. The second table called ResearchAssistant and has two columns: id and course. I designed the tables like this because there are different kinds of students and research assistant is one of them. The two table could be joint together with the primary key id.
I'm writing an endpoint /researchAssistant and take following content as request body of POST method.
{
"firstname":"Jack",
"lastname":"Peter",
"course":"MATH"
}
What I want is that saving firstname and lastname into StudentBase table and save course into ResearchAssistant table. And generate a same id for both.
The first idea comes to my mind is building 3 model classes: StudentBase(id, firstname, lastname), ResearchAssistant(id, course) and ResearchAssistantMixed(firstname, lastname, course). I use ResearchAssistantMixed class as the request body class. After getting the data I will seperate it into a new StudentBase object and a ResearchAssistant object, then I store them seperately.
This process seems really stupid and the performance should be quite low. Do you have some better ideas? How does Spring Boot deal with such cases? Thank you!
This is a database problem and not a spring-boot problem. This is how I would approach (I'm assuming you're using some relational DB like MySql and hibernate for ORM):
Database Tables:
student_base
- id (primary key)
- first_name
_ last_name
research_assistant
- id (primary key)
- student_base_id (foreign key referencing id of student_base)
- course
You can now have equivalent entity classes in Java (for hibernate):
#Entity
#Table(name = "student_base")
public class StudentBase {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
// getters and setters
}
#Entity
#Table(name = "research_assistant")
public class ResearchAssistant {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "student_base_id")
private StudentBase studentBase;
#Column(name = "course")
private String course;
// getters and setters
}
Now in your DAOs, you don't need to do much, just persist a student_base record and use the returned object to persist a research_assistant record. For example:
StudentBase studentBase = persist(new StudentBase(1, "abc", "xyz");
persist(new ResearchAssistant(1, studentBase, "pqr");
You can (and should) have two separate classes to accept the request object of the post API (don't use entity classes to accept request data).
I'm using Hibernate with a SQLite database. I have the following class :
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Authority {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int idAuthority;
(...)
I then have a class Author, that extends Authority and add 4 or 5 fields.
When I try to save an Author object, Hibernate generates the following request :
Hibernate: insert into Authority values ( )
And sqlite doesn't like that.
If I add a dummy String field, like "private String test" and set this property in the constructor, everything works fine.
I'm quite new to hibernate, so I'm not sure how to proceed. Do you have any idea ?
Edit : As requested, the mapping of the Author class :
#Entity
#Table(name="Authors")
#PrimaryKeyJoinColumn(name="idAuthority")
public class Author extends Authority {
#Column(name = "firstName")
protected String firstName;
#Column(name = "lastName")
protected String lastName;
#Column(name = "alias")
protected String alias;
(...)
}
Edit : As requested (bis), the insert code :
public void save(Object data) {
currentSession.saveOrUpdate(data);
}
Nothing fancy...
And to give you more possible leads, here is the database schema :
create table Authority (idAuthority integer, primary key (idAuthority))
create table Authors (alias varchar, firstName varchar,
lastName varchar, idAuthority bigint not null, primary key (idAuthority))
N.B. : in SQLite, an integer that is primary key is automatically set to AUTO-INCREMENT.
The Exception raised is this one :
java.sql.SQLException: near ")": syntax error
The request should be more like : insert into Authority values (NULL) to let SQLite do its auto-increment, not this weird "insert into Authority values ()".
Edit : This is definetely a problem with the SqlLite for Hibernate package. I just tried with Hsqldb, and it gives me a proper query :
insert into Authority (idAuthority) values (default)
(Then it fails too, but for very different reasons :p ).
I'm not sure there is a solution to this problem... other than using a different DB.
If you use InheritanceType.JOINED your table associated with class Authority must contain column associated with idAuthority and your table associated with class Author must contain column associated with idAuthority that is a foreign key to the primary identifier in table which presents Authority. It's required for table relations accociation
Try Overriding the ID.
#Entity
#Table(name="Authors")
#PrimaryKeyJoinColumn(name="idAuthority")
#AttributeOverride(name="idAuthority", column=#Column(name="id"))
public class Author extends Authority {
#Column(name = "firstName")
protected String firstName;
#Column(name = "lastName")
protected String lastName;
#Column(name = "alias")
protected String alias;
(...)
}
I'm trying to implement a very simple inheritance model in Hibernate. Basically I have a single superclass which can be called A, and several subclasses all of which inherit from A. Since the behavior I'm seeing is the same for all of them, they can just be referred to as B.
What I'm trying to arrive at is what's described here in section 6.2. Basically, there should be a table for A that contains its fields, and a table for B that contains only the fields that are distinct to the subclass, plus a join column back to the table for A. I am using Hibernate's automatic schema generation (enabled for the development persistence-unit only).
What I see when I look at the schema, however, is a table for A the contains its fields (correct), and a table for B which contains all the fields in A (incorrect), plus the fields added in B. My classes are annotated as follows:
#Entity
#Table(name="A")
#Inheritance(strategy = InheritanceType.JOINED)
public class A implements Serializable {
protected long id;
protected Date createDate;
protected String title;
protected boolean hidden;
public A() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
public Date getCreateDate() {
return createDate;
}
#Column(nullable = false)
public boolean isHidden() {
return hidden;
}
#Column(nullable = false)
public String getTitle() {
return title;
}
//also setters...
}
#Entity
#Table(name="B")
#PrimaryKeyJoinColumn(name="aId", referencedColumnName="id")
public class B extends A {
private String extraField;
public B() {
super();
}
#Column
public String getExtraField() {
return extraField;
}
//also setter...
}
Any ideas what I've done wrong? Specifically, what I want to see when I look at the generated DB schema is something like:
Table A: {id, createDate, title, hidden}
Table B: {aId, extraField}
...and instead what I get is:
Table A: {id, createDate, title, hidden}
Table B: {id, createDate, title, hidden, extraField}
Is this just not possible using Hibernate's automatic schema generation, or have I screwed up the annotations somewhere?
Your annotation is correct , it should produce the table schema that you want .
But now you get an undesired schema , which is exactly the schema produced using the Table per concrete class strategy (i.e #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)). So , I think one of the possible reason is that the hibernate.hbm2ddl.auto property in your configuration uses the default value , which is update .
The behavior of the update value is :
Hibernate will try to create an
update script to update the database
schema to the current mapping when
the SessionFactory is created.
If an update statement cannot be
performed , it will be skipped (For
example adding a not null column to a
table with existing data)
Hibernate will not delete any data
during the update .(For example , if
a column 's name is changed , it just
add an new column with the new name ,
but still keep the column with the
original name)
So , I think you must use #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) to generate the schema before , which produced the following schema . Table A and Table B do not have any foreign key associations to each other.
Table A: {id, createDate, title, hidden}
Table B: {id, createDate, title, hidden, extraField}
After that , you changed to use #Inheritance(strategy = InheritanceType.JOINED) . During the update schema process , hibernate just updated your scheme by adding a foreign key assocation between the TableA.id and TableB.id . It kept all other columns in Table B . That 's why you get the current schema even though your annotation is correct .
The desired table schema should be generated after you drop Table A and Table B from the DB before starting the hibernate programe . Alternatively , you can set the hibernate.hbm2ddl.auto to create , then hibernate will delete all tables before generating the table schema .
I am trying to learn Hibernate and I could create some simple CRUD operation using a Single Class and Single Table. I am just reading the Hibernate Doc and some online tutorial.
But I have a problem on how to define this relationship with two tables involved. I basically have an Employee table with this structure.
CREATE TABLE EMPLOYEE
(
EMP_ID VARCHAR(10) NOT NULL,
EMP_FIRST_NAME VARCHAR(30) NOT NULL,
EMP_LAST_NAME VARCHAR(30) NOT NULL,
STATUS_ID INT NOT NULL,
PRIMARY KEY (EMP_ID)
);
The STATUS_ID field references another table. STATUS_DESC can either be 'PERMANENT', 'CONTRACTUAL', 'ON-DEMAND'
CREATE TABLE EMP_STATUS
(
STATUS_ID VARCHAR(10) NOT NULL,
STATUS_DESC VARCHAR(100) ,
PRIMARY KEY (STATUS_ID)
);
I am thinking of having an Entity class like this. Now my goal is to return list of Employee object with status, but I don't know how to go about on doing this.
#Entity
public class Employee{
//other private instance
private EmployeeStatus empStatus;
//getters and setters.
}
public class EmployeeStatus{
private int statusID;
private String statusDesc;
//getters and setters
}
You want to know how to map it? ManyToOne?
Employee.java
#Entity
public class Employee{
//other private instance
#JoinColumn(name = "empStatus", referencedColumnName = "yourColName")
#ManyToOne(optional = false)
private EmployeeStatus empStatus;
//getters and setters.
}
Dont forget to change "referencedColumnName" value...
EmployeeStatus.java
#Entity
public class EmployeeStatus{
#Id //this is your pk?
private int statusID;
private String statusDesc;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "empStatus", fetch = FetchType.LAZY) //or EAGER
private List<Employee> empList;
//getters and setters
}
To create a relationship between two tables you need to decide:
Is the relationship bi-directional? That is, do the statuses know the employees or not? If no then it is uni-directional. In that case you can add the annotation on the Employee class like this:
#ManyToOne
#JoinColumn(name = "status")
private EmployeeStatus empStatus;
And there is a few other options that you may add.
You can do what you are doing, but I would suggest, if the status can only be one of three values, create an Enum with the three values. No need for a separate table.
The downside for this is you need to create a hibernate custom type (the code is on the wiki) to support persisting enums.
A simpler answer is to not use a secondary table, and just save the status as a String on the domain object. You can put business logic on your model to ensure the String is in the list of acceptable values.
If you really want to use a relationship between two entities, then check out the hibernate docs on many-to-one relationships.
You can use HQL to query the entities. Like so
Query q = s.createQuery("from Employee as e where e.empStatus = :status");
q.setParameter("status", status);
List emps= q.list();