Hibernate renames some columns at start with Spring Boot 1.4 - java

I'm having some problems with my PostgreSQL database columns on some tables.
If I declare a column of String with a name like a_column_name Hibernate accepts the column of the table with the name a_column_name.
But my problem comes when I declare the column type to Integer or BigDecimal. At start, Hibernate creates a new column of type int4 or numeric(19, 2) with a name like acolumnname with NULL's.
Hibernate: alter table t_myTable add column acolumnname int4
or
Hibernate: alter table t_myTable add column acolumnname numeric(19, 2)
I've tried to set the name-strategy on the config file of Spring boot:
jpa:
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: true
naming.physical-strategy: PhysicalNamingStrategyStandardImpl
But with no result...
So my question is why Hibernate accepts the column name when it's of String type but doesn't like it when it's Integer or BigDecimal.
Regards.
---- Update 1 ----
After search the Entity to post here the code as Veselin Davidov asked, I've noticed that some columns are accepted while others not.
The entities are created from a company framework that parses a JSON with the DB structure (tables, fields, type of fields...) and generates the JAVA entity class. So after see the answer from SAM I've changed the code of the Entity template to add a #Column(name = "your_column_name"). Now, when Hibernate starts, it doesn't add the columns with the wrong names and uses the DB columns.

if your Hibernate entity class is like the following:
#Entity
public class Registration{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
#Basic
private String name;
// getters and setters
}
then your registration table will have fields named with "id" and "name" as you have used auto-ddl=update. To avoid that you can specify the column names with #Column(name = "your_column_name") like below:
#Table(name = "registration")
#Entity
public class Registration{
#Id
#Column(name = "r_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Basic
#Column(name = "r_name")
private String name;
// getters and setters
}
If you are using Intellij IDEA then the IDE can generate the required Entity class.
You can go to your persistence tab -> right-click on your project name -> Generate Persistence Mapping -> By Database Schema. Then select the tables whose Entity you want to generate. Voila everything comes with ease.
Now coming to your problem, in your Entity class if you set some filed with Integer type then it will update your table to int4 and with allow null. The BigDecimal is also same numeric(10,2) with allow null. To avoid allow null in your database use primitive type int and double.

Related

Hibernate + JPA does not find the column for an #ElementCollection table

I am trying to add an #ElementCollection but the column is not found after the setup, so I constantly receive an error. I use Spring + flyway for the set up. Everything happens in the public schema
So here is my big object:
#Entity
#Table(name = "my_big_table")
MyBigObject{
#Id
#Column(name=COL_ID)
#GeneratedValue(generator="gen_name")
#GenericGenerator(
name = "gen_name",
strategy = "seq_name"
)
#AttributeAccessor(CommonConstants.HIBERNATE_ACCESS_PROPERTY)
private long id;
...
...
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "my_small_table",
joinColumns = #JoinColumn(name = "big_object_id")
)
private List<MySmallObject> mySmallObjects;
}
Here is my embedded object:
#Embeddable
public class MySmallObject {
#Column(name = "small_object_type")
private String smallObjectType;
}
Then besides the existing my_big_table table I add my_small_table using flyway
create table if not exists my_small_table
(
big_object_id bigint not null,
small_object_type varchar(64) not null
);
alter table my_small_table
add constraint FK_my_small_table
foreign key (big_object_id)
references my_big_table (id);
After this the my_small_table is successfully created but any instance of MyBigObject cannot be found because it looks for a column in the my_small_table that does not exist. As you can see it does not understand that the column name should use an underscore.
Big error trace ands with the following message:
Caused by: org.postgresql.util.PSQLException: ERROR: column mysmalltab0_.smallobjecttype does
not exist
09:17:24.994 INFO - STDOUT: Hint: Perhaps you meant to reference the column "mysmalltab0_.smallobjecttype".
Do you know what I could forget? Could lombock annotations that I also use for both classes spoil the picture?
As it's stated in the documentation:
By default, the placement of the #Id annotation gives the default access strategy. When placed on a field, Hibernate will assume field-based access. When placed on the identifier getter, Hibernate will use property-based access.
But the usage of the #AttributeAccessor leads to the changing access strategy for the field that hold #Id and as result your #Column(name = "small_object_type") annotation just was ignored. You can try to put it on the appropriate getter and it should work. But it's considered a good practiсe not to mix up access strategies for the entity fields.

Spring boot JPA: remove column on entity change

I am try to create table in MySQL database from java class using spring-boot-starter-data-jpa. It work pretty well except when I change/remove column name in java class. Here an example:
I have a class call "Staff" with 2 fields: id, name
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "name", length = 15)
private String name;
public Staff() {
}
// some setter and getter here
When I run my project, a "Staff" table generated exactly as I want with 2 columns: id, name. The problem is if I split "name" into "firstname" and "lastname" like this:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "firstname", length = 15)
private String firstname;
#Column(name = "lastname", length = 15)
private String lastname;
public Staff() {
}
//some getter and setter here
The "Staff" table now contain 4 columns (id, name, firstname, lastname) instead of 3. Then I need to remove the "name" column myself. Is there anyway to get rid of it automatically?
Looks like you use :
spring.jpa.properties.hibernate.ddl-auto=update
78.1 Initialize a database using JPA
spring.jpa.generate-ddl (boolean) switches the feature on and off and
is vendor independent. spring.jpa.hibernate.ddl-auto (enum) is a
Hibernate feature that controls the behavior in a more fine-grained
way. See below for more detail.
You can set spring.jpa.hibernate.ddl-auto explicitly and the standard
Hibernate property values are none, validate, update, create,
create-drop.
as you can see there is nothing new (different from hibernate)
ddl-auto=update - generate changes but it doen't drop not mapped columns .
For example you can have a table with 10 columns and mapping only for 3 of them , in this case auto mode might drop them but for hibernate / jpa full mapping table-entity in not mandatory. Here is jira ticket Alter and drop columns with hbm2ddl.auto=update created Nov 2011 and now status is 'not fixed'.
If you update db often (your domain model is changed) , you can use ddl/dml tools like liquibase , flywaydb. You describe db changes in xml file , and execute tool , all changes will be apply automatically (with auto control what already modifyed before and what should be modifed now).
Just recommendation :
Better use ddl tools if you don't want to guess why something is droped in production. hibernate.ddl-auto maily used for development , not for production. In production you can use none or validate - as they are safe.

Skip a column on INSERT with JPA

I have a JPA/EclipseLink model with multiple parameters whose values are set as defaults by the PostgreSQL database. Specifically, I have:
An id column of type SERIAL which auto-increments when new rows are added
A created_at column of type TIMESTAMP which defaults to now()
My model looks like this:
import javax.persistence.*;
#Entity
#Table(name="entity")
#NamedQuery(name="Entity.findAll", query="SELECT e from Entity e")
public class Entity {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="created_at")
private java.sql.Timestamp createdAt;
// constructor, getters, and setters
}
When I try to insert a row with persist() in javax.persistence.EntityManager, the insertion fails because a NULL value is being set for the created_at column. Rather than inserting NULL, I want to simply not insert anything into that column and allow PostgreSQL to set it to now(). Essentially, I would like to use #GeneratedValue on createdAt, but that annotation is only for primary keys and cannot be used on multiple attributes. Is there another annotation I can use that will accomplish this?
You may want insertable=false in the Column annotation:
#Column(name="created_at", insertable=false)
private java.sql.Timestamp createdAt;
From: http://docs.oracle.com/javaee/5/api/javax/persistence/Column.html
boolean insertable
(Optional) Whether the column is included in SQL INSERT statements generated by the persistence provide
You can add #DynamicInsert on your entity class. The insert statement will include null fields when without this annotation. Also, you can add # DynamicUpdate when executing the update statement.
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name="entity")
#NamedQuery(name="Entity.findAll", query="SELECT e from Entity e")
public class Entity {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="created_at")
private java.sql.Timestamp createdAt;
// constructor, getters, and setters
}
As mentioned in another answer, JPA providers will set the value on insert if you have it marked as insertable=true (the default). This occurs with many providers even if null as it allows statement reuse.
Just excluding it from the statement though might not be what you want, as to have the value in your entity (and in the cache) will require refreshing. EclipseLink though has #ReturningInsert which allows EclipseLink to update the entity with the value in the database. Unfortunately support is only for Oracle - other databases require use of stored procedures to return run the insert and return the value.

JPA secondary table as read only view - hibernate still tries to insert rows

I've got the following entity:
#Entity
#Table(name = "ONE")
#SecondaryTable(name = "VIEW_TWO", pkJoinColumns = #PrimaryKeyJoinColumn(name="ONE_ID"))
public class CpBracket {
#Id
private Long id;
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
(...)
}
As you see, this entity uses table ONE and (read only) view VIEW_TWO. When I'm persisting the entity, hibernate is performing insert into view:
insert into VIEW_TWO (ONE_ID) values (?)
It is ignoring the non-updatable and non-insertable column progress (that's good) and it is still trying to insert value of ONE_ID column. As far as I know, the annotation #PrimaryKeyJoinColumn marks selected column as insertable=false and updatable=false.
How can I prevent hibernate from inserting rows into secondary table (view)?
As far as I know, the annotation #PrimaryKeyJoinColumn marks selected
column as insertable=false and updatable=false.
I do not believe this can be the case: how then do we get records inserted into the #SecondaryTable when it is an actual table rather than a view?
As neither #SecondaryTable or #PrimarykeyJoinColumn have a means to prevent insert then it would appear that your original solution is not going to work and an alternative is required.
One option is to map VIEW_TWO as an #Entity and link to your class CPBracket as a #OneToOne relationship with cascade options set to none.
#Entity
#Table(name ="VIEW_TWO")
private CpBracketSummaryData(){
}
#Entity
#Table(name = "ONE")
public class CpBracket {
#OneToOne
#PrimaryKeyJoinColumn
private CPBracketSummaryData summaryData;
public int getSomeValue(){
return summaryData.getSomeValue();
}
}
The second option would be to use the non JPA compliant, Hibernate specific #Formula annotation.
#Entity
#Table(name = "ONE")
public class CpBracket {
#Formula("native sql query")
private int someValue;
}
Update October 2016
I have revisited this in both Hibernate 4.3.10.Final and 5.1.0.Final and it is possible to have the view as a #SecondaryTable without the insert: if you have the correct mappings.
Scenario 1
Load an entity for edit and do not touch any fields mapped to the secondary table. No update is issued to the secondary table
Scenario 2
Create and save a new entity and do not set any fields mapped to the secondary table. No insert is issued for the secondary table
Scenario 3
Create or update an entity including a field mapped to a secondary table and where this field is marked as insertable = false and updateable = false. An insert is made to the secondary table only for the ID field -the behaviour reported in the original question.
The issue with the mapping in the original question is the fact that the secondary table field is a primitive type and therefore when saving a new entity Hibernate does think a record has to be written to the secondary table with a value of zero.
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
The solution then is to replace primitives with the corresponding wrapper types and leave them as null. Then when saving a new record there is nothing to write to the secondary table and no insert will be made:
#Column(name="progress", table="VIEW_TWO")
private Integer progress;
I solved a similar problem with #SecondaryTable, which was a database view. So maybe it will help someone else.
The problem was on cascade delete to #SecondaryTable, when record from primary table was deleted.
As a solution, I implemented RULE on view for delete
CREATE RULE on_delete AS ON DELETE TO my_view DO INSTEAD(
select 1;
)
Similar solution can be used for INSERT and UPDATE operation on view.

checking uniq constraints without using primary key

I am using hibernate only with Annotations. My table looks something like this:
#Entity
#Table(name = "NetworkType",
uniqueConstraints = {#UniqueConstraint(columnNames = {"network_id", "type"})})
public class NetworkType implements Serializable {
#Id
private long id;
#Column(name = "network_id", nullable = false)
private long networkId;
#Column(name = "type", nullable = false)
private String type;
...
Currently when I write the same NetworkType twice, it throws an exception due to the UniqueConstraint (which is expected).
My thoughts are to just read the item first before checking. The problem is, my primary key is the Id, which I need because other tables references this table.
What's the best way to query for item for the "network_id" and "type" to verify the combination doesn't already exist?
I know I can do this with a Query manually, but is there a more Hibernate-y way of doing it?
In general, what's the proper way to "get" an object without using the PK? Are Criteria or Query the best way?
#UniqueConstraint is mainly used by database schema generation tools to create the data base schema. If used, they will generate the table with the columns mentioned in the #UniqueConstraint having unique constraint defined.
#UniqueConstraint doesn't have any impact/usage during data manipulation.
If you wish to achieve unique constraint behavior on network_id and type columns and your schema is already created, update your database schema to add the unique constraint on network_id and type columns. as below:
ALTER TABLE NetworkType
ADD CONSTRAINT uc_network_id_type UNIQUE (network_id, type)
Hope this helps!

Categories