JPA joined column allow every value - java

I'm testing JPA, in a simple case File/FileVersions tables (Master/Details), with OneToMany relation, I have this problem: in FileVersions table, the field "file_id" (responsable for the relation with File table) accepts every values, not only values from File table.
How can I use the JPA mapping to limit the input in FileVersion.file_id only for values existing in File.id?
My class are File and FileVersion:
FILE CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="FILE_ID")
private Long id;
#Column(name="NAME", nullable = false, length = 30)
private String name;
//RELATIONS -------------------------------------------
#OneToMany(mappedBy="file", fetch=FetchType.EAGER)
private Collection <FileVersion> fileVersionsList;
//-----------------------------------------------------
FILEVERSION CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="VERSION_ID")
private Long id;
#Column(name="FILENAME", nullable = false, length = 255)
private String fileName;
#Column(name="NOTES", nullable = false, length = 200)
private String notes;
//RELATIONS -------------------------------------------
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="FILE_ID", referencedColumnName="FILE_ID", nullable=false)
private File file;
//-----------------------------------------------------
and this is the FILEVERSION TABLE
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

Thanks for help,
I know the SQL constraint to limit the input, but it is possible to create this SQL costraint using some annotation, without writing by hand the SQL in the database?
I'm new on JPA, I was thinking that using #JoinColumn annotation, JPA could create also the costraint...
Thank you again.

At the Java level, you describe and annotate associations between classes - which and you did - and your mapping looks fine.
At the database level, if you want to restrict the possible values in the file_id column to values that are primary keys in the FILE table, you should use a foreign key constraint. To do so, you will need to use InnoDB tables. Something like that:
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
FOREIGN KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`) REFERENCES FILE(ID)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
The table FILE also has to use InnoDB. Actually, use InnoDB tables for the tables for which you want to use referential integrity.

Related

Hibernate ignoring changes in schema.sql

I'm developing an application with Spring Boot and hibernate, which connects to a postgres instance running in docker. When I first created my schema.sql, it looked like this:
CREATE TABLE groups(
group_id varchar(255) PRIMARY KEY,
group_desc varchar(255),
group_name varchar(255)
);
The table created successfully, however I soon realized 255 is too short for my purposes and changed my schema to the following:
CREATE TABLE groups(
group_id text PRIMARY KEY,
group_desc text,
group_name text
);
However, the database keeps reverting to the original data types. I've tried dropping the table, however when the Spring app runs and it gets created again as varchar(255) instead of text. How do I force hibernate to use the updated schema?
I've tried changing the spring.jpa.hibernate.ddl-auto property to create and update, and tried changing the fields to other datatypes, including other lengths of varchar. Nothing has worked so far. Even deleting schema.sql seemingly has no effect.
My application.properties looks like this:
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=<redacted>
spring.datasource.password=<redacted>
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
The Text datatype is not a Varchar but a CLOB.
Check your Groups class. I guess the name and desc attributes are String and the default related db type is then VARCHAR(255). Thus, if you generate your schema from your entity, String always become VARCHAR(255)
If you want to use Text, your field should be annotated with #Lob
public class Groups {
#Lob
#Column(name = "group_desc")
private String desc
#Lob
#Column(name = "group_name")
private String name
}
This being stated, I think you should change your java/db model because having a Lob/Text type as primary/foreign key frightens me a little (even if I never tried)
CREATE TABLE groups(
group_id bigint PRIMARY KEY,
group_code text NOT NULL UNIQUE,
group_desc text,
group_name text
);
public class Groups {
#Id
#Column(name = "group_id")
private Long id;
#Lob
#Column(name = "group_code", nullable = false, unique = true)
private String code;
#Lob
#Column(name = "group_desc")
private String desc;
#Lob
#Column(name = "group_name")
private String name;
}
NB : I usually don't generate the schema from entities so I instead use the code below to map a db text field to entity attribute :
#Column(name = "group_desc", columnDefinition = "CLOB")
private String desc;
But I am not sure if this is handled correctly to generate the schema

JPA insert identity id starts at 0 instead of 1?

I have a problem when I am inserting an new entity to a table in SQL Server, even when I set these annotation for my PK entity in java code.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
The index still started at 0, this case always happens when I drop and re-create table (when identity is set to null) and use insert by JPA repository. (index start at 1 when I used insert statement at Microsoft SSMS).
But when I delete table's data and use DBCC CHECKIDENT ('table', RESEED, 0);, then index of inserted record is 1.
Here is my CREATE statement:
CREATE TABLE [dbo].[tbl_member_info](
[id] [int] IDENTITY(1,1) NOT NULL,
[type] [int] NULL,
[id_card_number] [varchar](20) NULL,
[prefix] [int] NULL,
[first_name] [nvarchar](30) NULL,
[last_name] [nvarchar](30) NULL,
[dob] [datetime] NULL,
[email_personal] [varchar](70) NULL
)
Could anyone help me solve this problem?

Hibernate: How to define map values column name?

I have a class with map inside. It is mapped with use of join table.
#Entity
#Table(name = "Sources")
#Lazy(false)
public class Sources {
#ManyToMany( fetch = FetchType.EAGER )
#JoinTable( name = "sources_lists", joinColumns = #JoinColumn( name = "list_id" ) )
#MapKeyColumn( name = "source_id" )
public Map<Integer, Source> getSources() { return sources; }
public void setSources( Map<Integer, Source> sourcesList ) { this.adSources = adSourcesList; }
private Map<Integer, Source> sources;
#Override
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column( name="id", unique = true, nullable = false, updatable = false )
public Integer getId() { return super.getId(); }
}
I receive the following exception: "Unknown column 'sources0_.sources' in 'field list'".
When I change 'list_id' column name to the 'sources' things work, but I can't do this in production.
Tables are:
CREATE TABLE `sources` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`) );
CREATE TABLE `source` (
`DTYPE` varchar(31) NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
`className` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`));
CREATE TABLE `sources_lists` (
list_id` int(11) NOT NULL,
`source_id` int(11) NOT NULL,
KEY `FK54DCBD0B4307D0FC` (`source_id`),
KEY `FK54DCBD0B575FBECF` (`list_id`),
CONSTRAINT `FK54DCBD0B4307D0FC` FOREIGN KEY (`source_id`) REFERENCES `source` (`id`),
CONSTRAINT `FK54DCBD0B575FBECF` FOREIGN KEY (`list_id`) REFERENCES `sources` (`id`));
I realized that the issue was not with column, but with somethings else: I want to map Source objects by its id and Hibernate assumes that there are 3 columns in join table: parent object id (Sources class, list_id column), object id (Source class, source_id column) and a separate column for map key. I'll open other question to ask what is the way to map object in Hibernate by its id.

How to insert entity with foreign keys defined as Long in Hibernate?

I'm using Hibernate 3.6.8 and I have a table defined in mysql (5.5) like this:
CREATE TABLE mytable
(
id BIGINT(20) PRIMARY KEY NOT NULL,
version INT(11),
description VARCHAR(60),
scheduled_at DATETIME,
`from` BIGINT(20),
`to` BIGINT(20),
deleted BIT(1) DEFAULT b'0',
completed BIT(1) DEFAULT b'0',
delete_from_after_completion BIT(1) DEFAULT b'0',
CONSTRAINT FK14F71A73C588I54F FOREIGN KEY (`from`) REFERENCES other_table (id),
CONSTRAINT FK14F71C231C45J198 FOREIGN KEY (`to`) REFERENCES other_table (id)
);
CREATE INDEX FK14F71A73C588I54F ON mytable (`from`);
CREATE INDEX FK14F71C231C45J198 ON mytable (`to`);
And a Java entity defined like this:
#Entity
public class MyTable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#Column(name = "description")
private String description;
#Column(name = "scheduled_at")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "SM")
private Date scheduledAt;
#Column(name = "from")
private Long fromId;
#Column(name = "to")
private Long toId;
#Column(name = "deleted")
private boolean deleted;
#Column(name = "completed")
private boolean completed;
#Column(name = "delete_from_after_completion")
private boolean deleteFromEntityAfterCompletion;
...
}
When I try to persist an instance of MyTable with valid values I end up with the following error:
2016-01-12 10:06:33,443 [qtp2139431292-20] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1064, SQLState: 42000
2016-01-12 10:06:33,443 [qtp2139431292-20] ERROR org.hibernate.util.JDBCExceptionReporter - 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 'from, scheduled_at, to, version) values (0, 1, 0, 'Test', 10, '2016-01-13 00:00:' at line 1
I suspect that the problem occurs since I try to model the foreign key relationships (to and from) as Long instead of an entity (OtherTable). I suspect this because Hibernate can indeed persist this entity if I comment out the to and from fields. Note that the relationship to the to and from entities does indeed exists in the database so that's not the problem.
If I try insert manually using something like this it works:
insert into mytable values(3, 0, "desc", '2016-10-10 00:00:', 10, 11, 0, 0, 0);
You have a column that is a SQL reserved keyword ("from") and Hibernate doesn't bother quoting it for you. Other JPA implementations (e.g DataNucleus JPA) take care of such things for you. You will have to add single quotes around the reserved word in your JPA annotation information
I am pretty sure that the problem is not a matter of persisting Long values.
Your code has something wrong. I bet there is a wrong quotation mark in your insert code.

save XML to mysql using hibernate

I want to save an XML file into LONGTEXT field mysql through hibernate. I'm a bit new to hibernate and really appropriate your advice.
mysql table was created.
CREATE TABLE testdata (
TD_ID INT(11) NOT NULL AUTO_INCREMENT,
XML_VAL LONGTEXT NULL,
PRIMARY KEY (TD_ID)
Hibernate entity is also created.
#Lob
#Column(name="XML_VAL")
public String getXmlVal() {
return xmlVal;
}
public void setXmlVal(String xmlVal) {
this.xmlVal = xmlVal;
}
No need to use #Lob.
#Column(name = "XML_VAL", length = 65535)
You define the length of your text which needs to be inserted.

Categories