I am trying to add a unique constraint on a column but it works if I recreate database but doesn't work when I update already existing database.
Similar with adding unique to index. Is there a reason why hibernate doesn't update column if database already exists? I am using Dropwizard.
#Entity
#Table(name = EnrollmentStatusEntity.TABLE_NAME,
indexes = {
#Index(name = Student_RollNo,
columnList = rollNo,
unique = true)
})
#Data
public class Student {
public static final String TABLE_NAME = "Student";
#Id
#NonNull
#Column(name = ID, unique = true, updatable = false, nullable = false)
private String id;
#NonNull
#Column(name = COMPARTMENT_ID, unique=true, nullable = false, updatable = false)
private String rollNo;
Try to add this on your existing Hibernate class
settings.put(Environment.HBM2DDL_AUTO, "update");
it should work with this code but be careful when adding a table to the db if the table isnt NULL then it wont update the db (must be 0 on creating 1st time then change value inside the code or through the browser)
You can control the hibernate's automatic schema generation through your dropwizard project's configuration yaml file:
database:
properties:
hibernate.hbm2ddl.auto: update
This will update the schema automatically every time you run the application, if your db properties are otherwise correctly set in the configuration file.
This is usually not a good idea for production use.
Dropwizard Migrations uses Liquibase to track, version and deploy database changes. I would recommend using Dropwizard Migration and setting hibernate.hbm2ddl.auto: validate, that way you manage the database schema changes with Liquibase and validate with hibernate that the database schema matches the java entities.
Related
I am building my first Spring Boot application. I use Hibernate and an H2 in-memory DBMS.
What I am trying to build is a REST API that represents a number of App-Stores. I have an entity called App and another called Store. A store can contain many apps and each app can be contained in more than one store. Apps however, do not know in which stores they are contained. I want to be able to delete apps and stores independently of each other. Just because a store was deleted does not mean the apps therein should be deleted too and vice versa. Apps can exist without being in a store and stores without apps are fine too.
Here is the code for my entities, LpApp is the implementation for an App and LpTemplate is the implementation of a Store:
#Entity
public class LpApp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#NotBlank(message = "An app needs a non-empty name")
#Column(nullable = false, updatable = false, unique = true)
private String appName;
// ... constructors, getters, setters, no further annotations
}
#Entity
public class LpTemplate {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name = "template_apps",
inverseJoinColumns = { #JoinColumn(name = "app_id") },
joinColumns = { #JoinColumn(name = "template_id") })
private Set<LpApp> apps = new HashSet<>();
// ... constructors, getters, setters, no further annotations
}
This works well until I attempt to delete an App or Store from my DBMS. At this point I get an org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException.
The exception I get is the following (I trimmed the call stack for brevity):
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["APP_ID: PUBLIC.TEMPLATE_APPS FOREIGN KEY(APP_ID) REFERENCES PUBLIC.LP_APP(ID) (3)"; SQL statement:
delete from lp_app where id=? [23503-199]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation: "APP_ID: PUBLIC.TEMPLATE_APPS FOREIGN KEY(APP_ID) REFERENCES PUBLIC.LP_APP(ID) (3)"; SQL statement:
delete from lp_app where id=? [23503-199]
I am obviously doing something wrong, but I don't know where to look. I guess I am not using the #ManyToMany annotation right or perhaps it is the wrong annotation for my use case.
Thank you very much.
You need to add cascade attribute to tell hibernate not to delete the entity on delete operation. There are different options for cascade. Refer to this link to understand the different options.
The short version of the answer is that you are trying to delete a record that has an existing relationship with another record.
I have created an entity with table name and column names.
I have also added the uniquekey constraint for a column name. But when I run, it shows the following error ;
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error
executing DDL "alter table ingredient add constraint
UK_co7ro6kyijhfik027h0y4d3n3 unique (ingredient_name).
java.sql.SQLSyntaxErrorException: Specified key was too long; max key
length is 1000 bytes
After I run the spring boot application, I have tried to add the unique constraint manually in MySQL workbench. - DOES NOT WORK
I have added the below code - DOES NOT WORK
#Table(name = "ingredient", uniqueConstraints=#UniqueConstraint(name="uk_ingredient_name",columnNames="ingredient_name"))
#Column(name = "ingredient_name" ,unique = true)
private String ingredientName;
Tried to create a table manually in Mysql workbench and tried to alter the column name with unique key later. THIS WORKS. But I want hibernate to do this for me.
#Entity
#Table(name = "ingredient")
public class Ingredient {
#Id
#Column(name="ingredient_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "ingredient_name" ,unique = true)
private String ingredientName;
I want to save the ingredients without any repetitions. I do not want duplicate entries.
I have gone through other answers and none of those solutions helped me.
I tried adding length = 20 in the #column.
It works completely fine without any error.
Could you remove
,unique = true
from #Column annotation and try, it seems there is an hibernate bug .When using unique true it does not obey the naming strategy.
For more information look at this post
#UniqueConstraint and #Column(unique = true) in hibernate annotation
and this one
https://hibernate.atlassian.net/browse/HHH-11586
After removing the unique=true I am able to generate the unique constraint with the specified name that is 'uk_ingredient_name'.Since it is already specified in the #Table annotation.
The error you are getting is because hibernate is generating a unique constraint name which is hitting the allowed limit and it is not taking into the account the constraint name you have declared.
#Column(name="username", length=8, unique=true)
worked fine for me.
Method to store entity:
PoolDef poolDef = new PoolDef();
poolDef.setDate_from(date);
poolDef.setName(poolList.getPoolList().get(i).getName());
poolDefRepository.save(poolDef);
Entity itself:
#Setter
#Getter
#EqualsAndHashCode(of = {"Id"})
#Transactional
#Entity
public class PoolDef {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String name;
#Column(name = "date_from", columnDefinition = "timestamp with time zone not null")
private OffsetDateTime date_from;
}
The only value that varies is the date_from. I do not know, why new entries are being added to the database and not just get updated. I have the #EqualsAndHashcode that are being built based on primary Id key, so the date_from should not matter. Every method invocation creates a new entry with a completely new Id...
your problem is your
GenerationType.IDENTITY
the documentation says:
Indicates that the persistence provider must assign primary keys for
the entity using a database identity column.
Now the key question is, what SQL is getting generated. Can you please trace the insert SQLs generated?
Also I would prefer to use SEQUENCES of databases. So I do not have to mess around with the Identities of the persistence provider and any application will behave the same against the database if you use the SEQUENCES of databases for your ID/primary key columns.
Also a issue could be your DDL for the table creation, but I assume you know how to define the database tables with the given constraints.
This doesn't feel very DRY at all.
I have an SQL script that generates all of my database tables, so I have a lot of redundant annotations in my #Entity class.
For example,
#Column(unique = true, length = 254)
#NotNull
private String email;
or auto increment logic such as
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
All of these field annotations are redundant considering in the SQL I have already declared such things.
Is there any reason to keep these annotations around? From what I understand, any kind of serious application should be using SQL scripts to create the database tables anyways, and it sure would be nice to have less noise in the classes I'm writing.
there are two type of annotations (DDL and validation types) in your example:
#Column(unique = true, length = 254)
#NotNull
#NotNull is a JSR 303 Bean Validation annotation. It has nothing to do with database constraints itself. It's used by validation processor and doen't connected with DB. so it's not redundant in your mapping.
But , for example
#Column(nullable = false) - it gives the jpa provider hints to generate the right DDL for creating table columns with the database constraints
2.#Column(unique = true, length = 254) they are hints for ddl that hibernate generate.
BUT for #Column(updatable = false, name = "flight_name", nullable = false, length=50)
updatable - it's not ddl , it's optimization.
from hibernate docs
#Column(
ddl/mapping : name="columnName";
optimization: boolean insertable() default true;
optimization: boolean updatable() default true;
ddl/mapping : String table() default "";
DDL -->: boolean unique() default false;
DDL -->: boolean nullable() default true;
DDL -->: String columnDefinition() default "";
DDL -->: int length() default 255;
......
)
If you use ddl scripts all hibernate ddl-annotation attributes are redundant (if you use in memory tests , there will be autogenaration for in-memory db ), and will be used only if you configured hibernate.hbm2ddl.auto option in hibernate/jpa config. Also it's very dangerous scenarios when you give hibernate permission to do DDL in production.
for work with production : see liquibase , flywaydb for DDL.
Two reasons that come to my mind:
They can be useful in integration tests when you let hibernate create and drop schema in an in-memory database that is used in tests.
Very few of them are also used by Hibernate in runtime to make some optimizations, like optional = false (not null) in combination with #PrimaryKeyJoinColumn for one-to-one associations when Hibernate knows that it is safe to make a proxy for lazy association instead of switching to eager loading.
I want some of mycoulmns in the JPA entity to take values from database during inserts/updates, i.e from Oracle sys_context, I believe JPA temporal annotation includes database generated timestamps during insert/updates, Is there any way I could create a custom annotation somewhat simailar to this or provide some default values during inserts/updates, can someone give few pointers if you faced this situation.
I want some of mycoulmns in the JPA entity to take values from database during inserts/updates
Configure the #Column to be not insertable and updatable and annotate the field with #Generated (Hibernate specific annotation). By doing so, Hibernate will fetch the value from the database after an insert, update. From the Reference Documentation:
2.4.3.5. Generated properties
Some properties are generated at
insert or update time by your
database. Hibernate can deal with such
properties and triggers a subsequent
select to read these properties.
#Entity
public class Antenna {
#Id public Integer id;
#Generated(GenerationTime.ALWAYS)
#Column(insertable = false, updatable = false)
public String longitude;
#Generated(GenerationTime.INSERT) #Column(insertable = false)
public String latitude;
}
Annotate your property as #Generated
You have to make sure your
insertability or updatability does not
conflict with the generation strategy
you have chosen. When
GenerationTime.INSERT is chosen, the
property must not contains insertable
columns, when GenerationTime.ALWAYS
is chosen, the property must not
contains insertable nor updatable
columns.
#Version properties cannot be
#Generated(INSERT) by design, it has
to be either NEVER or ALWAYS.