I downloaded empty proj from spring initializr with web,jpa and postgres dependencies.
Added this config in application.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform = org.hibernate.dialect.PostgreSQL94Dialect
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
and created class Test, that's all I have.
#Entity
#Table(name = "tests")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 200)
private String author;
}
}
And then when the column's length parameter changes nothing happens in the database.
I tried to delete some columns and change some other parameters, result zero "alter table" SQL statements.
Update works only if new column added( update works only on this concrete new column only once).
from how does exactly spring.jpa.hibernate.ddl-auto property works in Spring?
The update operation for example will attempt to add new columns, constraints, etc but will never remove a column or constraint that may have existed previously but no longer does as part of the object model from a prior run.
if you want to change length after table is created, you have to use a Higher-level Database Migration Tool like flyway or liquibase.
Related
I am developed Rest API with sprint boot, jpa, hibernate and PostgreSQL. My goal is to be able to generate auto-incremented id for using with code and using database tool such as D Beaver without writing any extra queries for getting next ID value and etc.
I have created entity User. I tried generating id in two ways:
GenerationType.IDENTITY
When using GenerationType.IDENTITY it successfully creates table with name user, sequence with name user_id_seq and adds a default value for user.id column nextval('user_id_seq'::regclass). Everything in database is as expected and it works great with database tool, but problem occurs when I am trying to insert new row from my API. When trying to insert new row hibernate executes query
select currval('user_id_seq')
to get id value and I am getting error
org.postgresql.util.PSQLException: ERROR: invalid name syntax
because of those quotes around user. It should execute
select currval('user_id_seq')
I believe that the problem here is because I use table name user which is a reserved keyword, but I want to keep it this way because this naming matches other tables pattern.
GenerationType.SEQUENCE
If I use annotations:
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")
#SequenceGenerator(name="user_generator", sequenceName = "user_id_seq", allocationSize=1)
it creates table 'user', sequence 'user_id_seq' but doesn't add user.id column default value, so I can't insert new rows using database tool without specifying id value. But using this generation type my API works fine.
It is also worth mentioning that I am using spring.jpa.hibernate.ddl-auto=create-drop and manually dropping and recreating schema each time so there wouldn't be any unnecessary sequences/tables left.
#Entity
#Table(name = "`user`")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other properties...
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
// other getters and setters...
}
So... It is possible to somehow connect those two ways and create one working solution? I need to have default value for column and that hibernate would also know how to generate that id.
P.S. I don't want to change table/entity naming or execute SQL to correct tables when running application. I believe that there should be a better approach.
After a lot of hours of debugging I ended up extending PostgreSQL82Dialect and overriding getIdentitySelectString function to remove quotes from table name.
I'm using dropwizard-hibernate and postgres (hibernate version 5.3.7)
For my DTO i have a base DTO that contains an ID fields (all DTOS extend this class)
In the database schema the Id look like this
id uuid default gen_random_uuid() not null
My configuration for the ID is like this:
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid2")
private UUID id;
In theory this should work but every time that i try to persist an entity i get an error saying
ERROR: relation \"hibernate_sequence\" does not exist\
I've tried everything and nothing works.. i tried just with #Id and #GeneratedValue (according to the latest hibernate documentation that should be enough for the UUID config) and many other combination of annotations but every time I try to persist the entity i get the missing sequence issue.
I know I could "fix it" just adding the hibernate_sequence table in the database but I shouldn't need it at all.
I've used this & it's worked as expected:
#Column(name = "uid")
#Generated(GenerationTime.ALWAYS)
#Type(type = "pg-uuid")
private UUID uid;
I realize that #Generated is a legacy annotation, but it seems to work.
I'm using spring data (jpaRepository) + Oracle 11g Database.
Here's the code of my JUnit test:
#Test
public void testAjoutUtilisateur() {
Utilisateur utilisateur = new Utilisateur();
(...)
utilisateur=repository.save(utilisateur);
Utilisateur dbutilisateur = repository.findOne(utilisateur.getIdutilisateur());
assertNotNull(dbutilisateur);
When I debug I find that "utilisateur" object returned by repository.save method has an id like "2100" while the corresponding inserted line in the database have an id like "43".
I have an Oracle database with a sequence and a trigger to have the auto incremented property for the id for my "Utilisateur" table.
Here is the id definition in my Utilisateur entity:
#Entity
#NamedQuery(name="Utilisateur.findAll", query="SELECT u FROM Utilisateur u")
#SequenceGenerator(sequenceName="ID_UTILISATEUR_SEQ", name="ID_UTILISATEUR_SEQ")
public class Utilisateur implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ID_UTILISATEUR_SEQ")
private Long idutilisateur;
Where is the problem? Is it within the save method?
Thank you.
Edit:
I figured out that the problem was already solved by the solution of #jhadesdev and the data lines I was talking about were inserted when the triggers were actives.
Finally, I have to mention that by default the JUnit test seems to not insert data in the database (it inserts then rollback). In order to invalidate this behaviour we have to specify the #TransactionConfiguration(defaultRollback=false) annotation in the test class.
For example (in my case):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context/dao-context.xml" })
#TransactionConfiguration(defaultRollback=false)
#Transactional
public class UtilisateurRepositoryTest {
Hope it can help someone.
The problem is that two separate mechanisms are in place to generate the key:
one at Hibernate level which is to call a sequence and use the value to populate an Id column and send it to the database as the insert key
and another mechanism at the database that Hibernate does not know about: the column is incremented via a trigger.
Hibernate thinks that the insert was made with the value of the sequence, but in the database something else occurred. The simplest solution would probably be to remove the trigger mechanism, and let Hibernate populate the key based on the sequence only.
I have a many-to-many relationship where the link table has an additional property. Hence the link table is represented by an entity class too and called Composition. The primary key of Composition is an #Embeddable linking to the according entities, eg. 2 #ManyToOne references.
It can happen that a user makes an error when selecting either of the 2 references and hence the composite primary key must be updated. However due to how JPA (hibernate) works this will of course always create a new row (insert) instead of an update and the old Composition will still exist. The end result being that a new row was added instead of one being updated.
Option 1:
The old Composition could just be deleted before the new one is inserted but that would require that the according method handling this requires both the old and new version. plus since the updated version is actually a new entity optimistic locking will not work and hence last update will always win.
Option 2:
Native query. The query also increments version column and includes version in WHERE clause. Throw OptimisticLockException if update count is 0 (concurrent modification or deletion)
What is the better choice? What is the "common approach" to this issue?
Why not just change the primary key of Composition to be a UID which is auto-generated? Then the users could change the two references to the entities being joined without having to delete/re-create the Composition entity. Optimistic locking would then be maintained.
EDIT: For example:
#Entity
#Table(name = "COMPOSITION")
public class Composition {
#Id
#Column(name = "ID")
private Long id; // Auto-generate using preferred method
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private FirstEntity firstEntity;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private SecondEntity secondEntity;
....
In already existing table structure inheritance I am adding a new column type (I cut some of the code)
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Account {
......
#Column // already existed column
private String name; // get/set also applied
#Column(length=20) // new added column
#Enumerated(EnumType.STRING) // get/set also applied
private AccountType type;
..........
}
#Entity
public User extends Account {
................ // some other already existed fields
}
In my persistence.xml file I am using next strategy policy for DDL generation
property name="eclipselink.ddl-generation" value="drop-and-create-tables"
When DDL generation is processing the new added column type in Account table is successfully created, BUT for User table there is no such kind of column at all (the strategy is TABLE_PER_CLASS).
I fixed that when i drop the database and created it again. After that the current generation of DLL was applied - type in User is also added as a column. Does someone "met" with such kind of issue ? I fixed with with drop and create of the DB but I am not sure that should be the strategy in same cases in future, specially for production DB
Thanks,
Simeon Angelov
DDL generation is for development not production. The problem you are seeing is because when the table already exists, it cannot be created with the new field. Drop and create or the "create-or-extend-tables" feature will work if you are adding to the tables as described here http://wiki.eclipse.org/EclipseLink/DesignDocs/368365