I'm running integrity tests on a Spring Boot 2.1.4 project with JUnit 4.12 and I have run into a problem running a save (Comment) endpoint test in a bidirectional relationship of type #OneToOne - #ManyToOne when I use the #JsonManagedReference and #JsonBackReference annotations to avoid recursion.
The problem is that when I run the test it isn't converting the parent (Author class) of the relation to JSON, only the child (Comment class) is converted, but with none reference to parent.
Object of class Comment received to convert it to JSON
Result of convert the object of class Comment to JSON, where is the parent?
DDL of tables in a H2 Database
CREATE TABLE IF NOT EXISTS `author` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`created` TIMESTAMP NULL,
`modified` TIMESTAMP NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `comment` (
`id` INT NOT NULL AUTO_INCREMENT,
`review` VARCHAR(255) NOT NULL,
`author_id` INT NOT NULL,
`created` TIMESTAMP NULL,
`modified` TIMESTAMP NULL,
PRIMARY KEY (`id`),
INDEX `fk_comment_author_idx` (`author_id` ASC),
CONSTRAINT `fk_comment_author` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE = InnoDB;
Parent entity
#JsonManagedReference("author_comment")
#OneToMany(mappedBy = "author", cascade = {CascadeType.ALL}, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
Child entity
#JsonBackReference("author_comment")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private Author author;
Related
I have the following two tables in my database:
CREATE TABLE ACCOUNT (
ID NUMBER(19, 0) NOT NULL,
AA_ID VARCHAR2(11 CHAR),
BB_ID VARCHAR2(8 CHAR),
STATUS NUMBER(1, 0) DEFAULT 1,
CREATED_AT TIMESTAMP(6) NOT NULL,
LAST_MODIFIED_AT TIMESTAMP(6)
) TABLESPACE $tbsp;
CREATE TABLE MOBILE_INSTANCE (
ID NUMBER(19, 0) NOT NULL,
ACCOUNT_ID NUMBER(19, 0) NOT NULL,
APPLICATION_ID VARCHAR2(36 CHAR) NOT NULL,
TOKEN VARCHAR2(255 CHAR) NOT NULL,
DEVICE VARCHAR2(13 CHAR),
STATUS VARCHAR2(10 CHAR),
CREATED_AT TIMESTAMP(6) NOT NULL,
LAST_MODIFIED_AT TIMESTAMP(6)
) TABLESPACE $tbsp;
And the connection between them:
ALTER TABLE MOBILEBANK_INSTANCE ADD CONSTRAINT FK_MOBILEBANKINSTANCE_ACCOUNT FOREIGN KEY (ACCOUNT_ID) REFERENCES ACCOUNT(ID);
So far, this is working like a charm...
Via JPA, I have created the entites for these tables - please see the following snippet of my entities:
In the MobileInstance entity I have marked the #ManyToOne connection as following:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ACCOUNT_ID")
private Account accountId;
And this is how it looks like in my Account entity class:
#OneToMany(mappedBy = "accountId", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<MobileInstance> mobileInstanceList = new ArrayList<>();
As you see, the accountId field (which is in the Account table actually a Long value) is marked as an Account object.
Later on, when I have already an account object, I whish the create a mobileInstance object as well, with the appropiate account ID.
But when I call:
mobileInstance.setAccountId
it requieres an account parameter... When I assigne one to it and try to save it in the DB, I got the error:
ERROR SqlExceptionHelper:131 - ORA-00904: "APPLICATION_ID": invalid identifier
I assume, it is because the Application ID field in the Database is a Number field, and I assigne to it a whole Account object which I do not want to change to Long..
Is there a way the code to recognize that only use the primary key of my Account table?
My POJO is using JPA and when I apply unique value in the column is not working for me I tried to use the UniqueConstraint and also not working for me .
below is my code
#Entity
#Table(name = "users", uniqueConstraints={#UniqueConstraint(columnNames = {"user_id"}),#UniqueConstraint(columnNames = {"username"}),#UniqueConstraint(columnNames = {"email"})})
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id",unique=true,nullable = false)
private int UserId;
#Column(name = "username" ,unique=true,nullable = false)
private String Username;
#Column(name = "email",unique=true ,nullable = false)
private String email;
#Column(name = "firstname",nullable = false)
private String firstname;
#Column(name = "lastname", nullable = false)
private String lastname;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "active")
private int active;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_role", joinColumns=#JoinColumn(name="user_id"), inverseJoinColumns=#JoinColumn(name="role_id"))
private Set<Role> roles;
below is the generated table in the database (MySQL)
| users | CREATE TABLE users (
user_id int(11) NOT NULL,
username varchar(255) NOT NULL,
active int(11) DEFAULT NULL,
email varchar(255) NOT NULL,
firstname varchar(255) NOT NULL,
lastname varchar(255) NOT NULL,
password varchar(255) NOT NULL,
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
Hibernate log after spring start
Hibernate: create table users (user_id integer not null, username varchar(255) not null, active integer, email varchar(255) not null, firstname varchar(255) not null, lastname varchar(255) not null, password varchar(255) not null, primary key (user_id)) engine=InnoDB
Hibernate: alter table users drop index UKfnranlqhubvw04boopn028e6
Hibernate: alter table users add constraint UKfnranlqhubvw04boopn028e6 unique (username, email)
Hibernate: alter table users drop index UK_r43af9ap4edm43mmtq01oddj6
Hibernate: alter table users add constraint UK_r43af9ap4edm43mmtq01oddj6 unique (username)
Hibernate: alter table users drop index UK_6dotkott2kjsp8vw4d0m25fb7
Try like this:
#Entity
#Table(name="users",uniqueConstraints=#UniqueConstraint(columnNames={"user_id","username","email"}))
public class Users {
Apparently there's nothing wrong with this code. unique=true in #Column is a shortcut for #UniqueConstraint(columnNames = {"user_id"} and other particular constraints. You could only use one of them except for multiple unique constraints.
But if you are confused why the generated CREATE TABLE doesn't contain these unique constraints, check the log, you can find alter table commands just after CREATE TABLE.
In this case commands could be,
alter table users add constraint UK_some_id unique (username)
alter table users add constraint UK_some_id unique (email)
Hope this help.
I had the same problem here, set length to your unique columns.. It worked here. Do
#Column(name = "userName", length = 50, unique = true)
if you set unique in #Column
that's mean let your JPA provider create the database for you - it will create the unique constraint on the specified column.
But after creating a database, or you alter it once created,
then unique doesn't have any effect
in jpa the id is already unique so to make username and email unique add the statement
unique=true
to the corresponding #Column annotation.
PS : remember the unique cannot be null when insert new item you should drop the table before run the application and remove uniqueConstraints in table annotation
Set the column to have the fixed length make it work because mysql size for unique constrains limited to 1000 bytes
#Column(name = "username", length = 200, unique = true)
Use #javax.persistence.Column(unique = true).
Dont forget to restart your application.
&& you cant alter your column if there are still duplicated values in it.
drop your table, and recreate it with your generated ddl. (if ure not usin springboot)
&& if youre using springboot, go to your application.properties & set spring.jpa.hibernate.ddl-auto to update
This updates the schema if necessary.
The column needs to be non nullable for it to be unique!
#Column(unique = true) will NOT work
#Column(unique = true, nullable = false) will work
If the database or schema already exists before adding the uniqueConstraints then it won't work.
So drop the database or schema and run the application to create new database with these uniqueConstraints
DROP DATABASE database_name;
Does anybody knows how to set the constraint name for primary key (PK_[name]) ,check (CK_[Name]),Default [DF_[Name]], Foreign Key (Fk_[Name]) ,Unique ..
by using Annotations or xml config for example i want that Table ::::
CREATE TABLE ACCOUNT (
[ID] INT NOT NULL IDENTITY(1,1),
[USERNAME] VARCHAR(50) NOT NULL,
[PASSWORD] VARCHAR(50) NOT NULL,
[EMAIL] VARCHAR(100) NOT NULL,
[GENDER] INT NOT NULL ,
[ADDRESS] INT,
[AGE] INT CONSTRAINT DF_tblAccount_Age DEFAULT 0,
CONSTRAINT PK_tblAccount_ID PRIMARY KEY([ID]),
CONSTRAINT UQ_tblAccount_Address UNIQUE(ADDRESS),
CONSTRAINT FK_tblAccount_Gender FOREIGN KEY([GENDER]) REFERENCES GENDER([ID]),
CONSTRAINT FK_tblAccount_Address FOREIGN KEY([ADDRESS]) REFERENCES ADDRESS([ID]) ON DELETE SET NULL,
CONSTRAINT CK_tblAccount_Age CHECK ([AGE] > 0 AND [AGE] <100)
)
Translate it into annotation class or [name].hbm.xml ,
does Hibernate support to use constraint name or i must alter the tables
every time i create a new class ...
I searched and i find 0 results about constraint names !!
Annotation Class Example :::
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="ID")
// -->>Add Primary Key constraint Name Here !! ???
private int id;
#Column(name="Name")
private String name;
#Column(name="Surname")
private String surname;
#Column(name="age")
// -->>Add Check constraint Name Here !! ???
private String age;
#OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
#OnDelete(action=OnDeleteAction.CASCADE)
#JoinColumn(name="Address" , referencedColumnName="id",nullable=false)
// -->>Add Foreign Key constraint Name Here !! ???
private Address address;
In JPA, you can do this at class level, using #Table annotation properties, where you specify constraints names. For primary key id mapped to column id:
#Table(uniqueConstraints = #UniqueConstraint(columnNames = "ID", name = "PERSONS_PK_CONSTRAINT"))
I want to persist a objects by using JPA in a MYSQL-database. Here ist my create script:
CREATE TABLE toolboxAccount (
idtoolboxAccount INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(30) NOT NULL ,
password_2 VARCHAR(30) NOT NULL ,
PRIMARY KEY(idtoolboxAccount));
CREATE TABLE config (
idconfig INT NOT NULL AUTO_INCREMENT,
toolboxAccount_idtoolboxAccount INTEGER UNSIGNED NOT NULL ,
defaultExportPath VARCHAR(255) NULL ,
PRIMARY KEY(idconfig) ,
INDEX config_FKIndex1(toolboxAccount_idtoolboxAccount),
FOREIGN KEY(toolboxAccount_idtoolboxAccount)
REFERENCES toolboxAccount(idtoolboxAccount)
ON DELETE CASCADE
ON UPDATE CASCADE);
CREATE TABLE connection (
idconnection INT NOT NULL AUTO_INCREMENT,
toolboxAccount_idtoolboxAccount INTEGER UNSIGNED NOT NULL ,
url VARCHAR(255) NULL ,
username VARCHAR(30) NULL ,
password_2 VARCHAR(30) NULL ,
site VARCHAR(30) NULL ,
PRIMARY KEY(idconnection) ,
INDEX connection_FKIndex1(toolboxAccount_idtoolboxAccount),
FOREIGN KEY(toolboxAccount_idtoolboxAccount)
REFERENCES toolboxAccount(idtoolboxAccount)
ON DELETE CASCADE
ON UPDATE CASCADE);
CREATE TABLE export (
idexport INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
connection_idconnection INT NOT NULL ,
nameOfExportZIP VARCHAR(255) NOT NULL ,
PRIMARY KEY(idexport) ,
INDEX export_FKIndex1(connection_idconnection),
FOREIGN KEY(connection_idconnection)
REFERENCES connection(idconnection)
ON DELETE CASCADE
ON UPDATE CASCADE);
The classes are
ToolboxaccountEntity
#Entity
#Table(name = "toolboxaccount", schema = "", catalog = "toolboxtest")
public class ToolboxaccountEntity {
#Id
#GeneratedValue(generator="my_seq")
#SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)
private long idtoolboxAccount;
private String username;
private String password_2;
#OneToOne(cascade = CascadeType.PERSIST)
private ConfigEntity configEntity;
#OneToMany(cascade = CascadeType.PERSIST)
private List<ConnectionEntity> connections;
public List<ConnectionEntity> getConnections() {
return connections;
}
public void setConnections(List<ConnectionEntity> connections) {
this.connections = connections;
}
...
ConnectionEnity
#Entity
#Table(name = "connection", schema = "", catalog = "toolboxtest")
public class ConnectionEntity {
#Id
#GeneratedValue(generator="my_seq")
#SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)
private long idconnection;
private String url;
private String username;
private String password_2;
private String site;
#Column(name = "idconnection")
public long getIdconnection() {
return idconnection;
}
public void setIdconnection(int idconnection) {
this.idconnection = idconnection;
}
...
The persisting of ToolboxAccountEntity works but when I add a ConnectionEntity I get following error:
Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`toolboxtest`.`connection`, CONSTRAINT `connection_ibfk_1` FOREIGN KEY (`toolboxAccount_idtoolboxAccount`) REFERENCES `toolboxaccount` (`idtoolboxAccount`) ON DELETE CASCADE ON UPDATE CASCADE)
Error Code: 1452
Call: INSERT INTO toolboxtest.connection (PASSWORD_2, SITE, URL, USERNAME) VALUES (?, ?, ?, ?)
bind => [4 parameters bound]
2 things:
1) If you want to make a insert operation, make sure that the record doesnt exists in the Database.
2) If you want to make an update operation, make sure that the record exist in the database.
3) Remember that the records has to be well relationships, primary key, foreign key.
That kind of error, is very common when a record already exists.
Regards! Tell us if you correct your issue
I'm having trouble using NATURAL JOIN in Hibernate with #Formula.
I have three tables:
CREATE TABLE IF NOT EXISTS `tz_points_logs` (
`points_log_id` bigint(20) NOT NULL,
`points_to_uid` bigint(20) NOT NULL,
`points_get_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`points_rule_id` int(4) NOT NULL,
`points_meta` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `tz_points_rules` (
`points_rule_id` int(4) NOT NULL,
`points_rule_credits` int(4) NOT NULL,
`points_rule_title` varchar(32) NOT NULL,
`points_rule_description` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `tz_users` (
`uid` bigint(20) NOT NULL,
`username` varchar(16) NOT NULL,
`password` varchar(32) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In Hibernate, there're three mapping Models: User, PointsLog and PointsRule.
Now, there a #Formal field in User Model:
#Entity
#Table(name = "tz_users")
public class User implements Serializable {
// Getters and Setters
#Id
#GeneratedValue
#Column(name = "uid")
private long uid;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#OneToMany(targetEntity = PointsLog.class,
fetch = FetchType.LAZY, mappedBy = "user")
private List<PointsLog> pointsLogs = new ArrayList<PointsLog>();
#Formula(value = "(SELECT COUNT(*) FROM tz_points_logs pl NATURAL JOIN tz_points_rules WHERE pl.points_to_uid=uid)")
private long credit;
}
There's an error while get credit of the User:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
near 'user0_.NATURAL JOIN tz_points_rules WHERE pl.points_to_uid=user0_.uid) as formul' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
at com.mysql.jdbc.Util.getInstance(Util.java:360)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:978)
...
at java.lang.Thread.run(Thread.java:745)
It seems that the Hibernate cannot recognize NATURAL JOIN in #Formula.
Is there any other method to implement this function?
BTW, following statement works fine.
#Formula(value = "(SELECT COUNT(*) FROM tz_points_logs pl WHERE pl.points_to_uid=uid)")
Thanks a lot.
Similar issue is discussed in this answer. The point here is - Hibernate treats the NATURAL keyword as a property/column name. A workaround should be - register NATURAL as a keyword in the dialect.
For example:
// extend the correct dialect ... in this case MySql
// but simply extend the currently used dialect
public class CustomMySQLDialect extends MySQL5InnoDBDialect
{
public CustomMySQLDialect()
{
super();
registerKeyword("NATURAL");
}
}
Next configure Hibernate to be using this CustomMySQLDialect, and the FORMULA should be generated:
// instead of this
// user0_.NATURAL JOIN
// we should get this
NATURAL JOIN