I'm migrating an app to use CockroachDB and we are using the GeneratedValue mapping in Java with SERIAL type columns to manage primary keys.
id SERIAL PRIMARY KEY -- SQL
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) -- Java JPA
We are getting the following error
ERROR: currval(): relation "scm_supply_centers_id_seq" does not exist
We dug a little and found out that it's because of PostgreSQL Dialect trying to get the last id inserted in as you can see in this link
PostgreSQL81IdentityColumnSupport
How can I find a workaround for this issue?
The SERIAL type is not backed by a sequence in CockroachDB. In order to use a SEQUENCE you need to explicitly create it and use the nextval() function as the DEFAULT value for the column. For example,
CREATE SEQUENCE customer_seq;
CREATE TABLE customer_list (
id INT PRIMARY KEY DEFAULT nextval('customer_seq'),
customer string,
address string
);
Note that there are performance implications to using SEQUENCE vs SERIAL due to the additional synchronization and communication requirements. See https://www.cockroachlabs.com/docs/stable/create-sequence.html for more details.
The above doesn't exactly answer your question, though. I don't know how you would use an explicit sequence from Hibernate.
Related
Before I added Flyway to my project, I could run POST request and the new user was created successfully with ID = 1, next one ID = 2 etc.
Then I added Flyway to create tables and insert some test data by V1_init.sql:
create table "user"(
id int8 not null,
username varchar(255),
);
insert into "user" values (1, 'user1');
insert into "user" values (2, 'user2');
insert into "user" values (3, 'user3');
Table is created. Users are inserted.
Trying to run POST request -> error 500
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "organisation_pkey" Key (id)=(1) already exists.
So my app should add new user with ID=4 but it looks like it can't recognize that there are 3 users already added.
I'm using GenericEntity:
#Getter
#Setter
#MappedSuperclass
public abstract class GenericEntity<ID extends Serializable> implements Serializable {
#Id
#GeneratedValue
protected ID id;
}
application.properties:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/my-app
spring.datasource.username=user
spring.datasource.password=user
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true
I tried to use all strategies #GeneratedValue, changing spring.jpa.hibernate.ddl-auto, adding users in init.sql without id (not working)
but still no positive effects. Any ideas what could be wrong?
You seem to have only a half understanding of what you're doing...
I tried to use all strategies #GeneratedValue
You don't need to randomly try strategies, you need to pick the one that matches your current database design.
changing spring.jpa.hibernate.ddl-auto
This is dangerous and you should set it to "none", given that you are using flyway.
adding users in init.sql without id (not working)
This will only work if postgresql is set up to automatically generate ids (which is easiest through a sequence).
From your code, it does not look like that is the case.
what could be wrong?
JPA's #GeneratedValue is capable of ensuring that values are generated when it is responsible for creating rows (that means when you pass EntityManager#persist). It does not and can not know about your flyway scripts where you bypass JPA to insert rows manually.
Furthermore, let's look at #GeneratedValue's strategy property. The strategy you choose will influence how JPA generates IDs. There are only a few options: TABLE, SEQUENCE, IDENTITY and AUTO. Since you did not explicitly specify a strategy, you are currently using the default, which is AUTO. This is not recommended because it is not explicit, and now it's hard to say what your code is doing.
Under the TABLE and SEQUENCE strategies, JPA will do an interaction with the database in order to generate an ID value. In those cases, JPA is responsible for generating the value, though it will rely on the database to do so. Unsurprisingly, the former will use a table (this is rare, btw, but also the only strategy that is guaranteed to work on all RDBMS) and the latter will use a sequence (far more common and supported by practically every commercially relevant RDBMS).
With IDENTITY, JPA will not attempt to generate a key at all, because this strategy assumes that the DB will generate an ID value on its own. The responsibility is thus delegated to the database entirely. This is great for databases that have their own auto-increment mechanism.
Postgres does not really have an auto-increment system but it has some nice syntactic sugar that nearly makes it work like it: the serial "datatype". If you specify the datatype of a column as "serial", it will in fact be created with datatype int, but postgresql will also create a sequence and tie the default value of the ID column to the sequence's next value generator.
In your case, JPA is most likely using either SEQUENCE or TABLE. Since your DDL setting is set to "update", Hibernate will have generated a table or sequence behind your back. You should check your database with something like pgAdmin to verify which it is, but I'd put my money on a sequence (so I'm assuming it's using the SEQUENCE strategy).
Because you haven't specified a #SequenceGenerator, a default will be used which, AFAIK, will start from 1.
Then when JPA tries to insert a new row, it will call that sequence to generate an ID value. It will get the next value of the sequence, which will be 1. This will conflict with the IDs you manually entered in flyway.
My recommended solution would be to:
redefine your postgresql data type from int8 to "serial" (which is actually int + a sequence + sets up default value linking the ID column to the sequence so that postgres will automatically generate an ID if you don't explicitly specify one - careful, also don't specify null, just don't specify the ID column in the insert statement at all!)
explicitly set the generator strategy to IDENTITY on the JPA side
update your flyway scripts to insert users without explicit ID value (this will ensure that the test data advance the sequence, so that when JPA uses that same sequence later, it will not generate a conflicting ID)
I'd say there are alternative solutions, but other than using the TABLE strategy or generating keys in memory (both things which you should avoid), there isn't really a viable alternative because it will boil down to using a sequence anyway. I suppose it's possible to manually specify the sequence, forego the default value on the id field, call the sequence manually in your insert statements, and map the sequence explicitly in JPA... but I don't see why you'd make things hard on yourself.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Why we are using this annotations?
i need to know if this autoincrement my table id values.
(GenerationType.IDENTITY) is there any other types whats actually happening when we use this annotation
public class Author extends Domain
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "authorId")
private List<Book>
bookList;
public Author()
{
setServiceClassName("wawo.tutorial.service.admin.AuthorService");
}
}
*Is it necessary to extend Domain abstract class?What is the use?
First of all, using annotations as our configure method is just a convenient method instead of coping the endless XML configuration file.
The #Idannotation is inherited from javax.persistence.Id, indicating the member field below is the primary key of current entity. Hence your Hibernate and spring framework as well as you can do some reflect works based on this annotation. for details please check javadoc for Id
The #GeneratedValue annotation is to configure the way of increment of the specified column(field). For example when using Mysql, you may specify auto_increment in the definition of table to make it self-incremental, and then use
#GeneratedValue(strategy = GenerationType.IDENTITY)
in the Java code to denote that you also acknowledged to use this database server side strategy. Also, you may change the value in this annotation to fit different requirements.
1. Define Sequence in database
For instance, Oracle has to use sequence as increment method, say we create a sequence in Oracle:
create sequence oracle_seq;
2. Refer the database sequence
Now that we have the sequence in database, but we need to establish the relation between Java and DB, by using #SequenceGenerator:
#SequenceGenerator(name="seq",sequenceName="oracle_seq")
sequenceName is the real name of a sequence in Oracle, name is what you want to call it in Java. You need to specify sequenceName if it is different from name, otherwise just use name. I usually ignore sequenceName to save my time.
3. Use sequence in Java
Finally, it is time to make use this sequence in Java. Just add #GeneratedValue:
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
The generator field refers to which sequence generator you want to use. Notice it is not the real sequence name in DB, but the name you specified in name field of SequenceGenerator.
4. Complete
So the complete version should be like this:
public class MyTable
{
#Id
#SequenceGenerator(name="seq",sequenceName="oracle_seq")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
private Integer pid;
}
Now start using these annotations to make your JavaWeb development easier.
In a Object Relational Mapping context, every object needs to have a unique identifier. You use the #Id annotation to specify the primary key of an entity.
The #GeneratedValue annotation is used to specify how the primary key should be generated. In your example you are using an Identity strategy which
Indicates that the persistence provider must assign primary keys for
the entity using a database identity column.
There are other strategies, you can see more here.
Simply, #Id: This annotation specifies the primary key of the entity.
#GeneratedValue: This annotation is used to specify the primary key generation strategy to use. i.e Instructs database to generate a value for this field automatically. If the strategy is not specified by default AUTO will be used.
GenerationType enum defines four strategies:
Generation Type . TABLE,
Generation Type. SEQUENCE,
Generation Type. IDENTITY
Generation Type. AUTO
GenerationType.SEQUENCE
With this strategy, underlying persistence provider must use a database sequence to get the next unique primary key for the entities.
GenerationType.TABLE
With this strategy, underlying persistence provider must use a database table to generate/keep the next unique primary key for the entities.
GenerationType.IDENTITY This GenerationType indicates that the persistence provider must assign primary keys for the entity using a database identity column. IDENTITY column is typically used in SQL Server. This special type column is populated internally by the table itself without using a separate sequence. If underlying database doesn't support IDENTITY column or some similar variant then the persistence provider can choose an alternative appropriate strategy. In this examples we are using H2 database which doesn't support IDENTITY column.
GenerationType.AUTO This GenerationType indicates that the persistence provider should automatically pick an appropriate strategy for the particular database. This is the default GenerationType, i.e. if we just use #GeneratedValue annotation then this value of GenerationType will be used.
Reference:- https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/jpa-primary-key.html
Why are we using this annotation?
First I would like to remind everyone that the annotations, such as #Id, are providing metadata to the persistence layer(I will assume hibernate).This metadata will most likely be stored in the .class file(but not stored in the database) and is used to tell hibernate how to recognize, interpret and manage the entity. So, Why are you using the annotation? To provide your persistence layer with the proper information about how to manage the entity.
Why use the #Id annotation?
The #Id annotation is one of the two mandatory annotations needed when creating an entity with JPA. The other one being #Entity. #Id does two things for us:
1) signifies that this field will be the unique identifier for this class when mapped to a database table
2) the presence of #Id lets the persistence layer know that all other fields within this class are to be mapped to database rows
Why use #GeneratedValue?
By marking the #Id field with #GeneratedValue we are now enabling id generation. Which means that the persistence layer will generate an Id value for us and handle the auto incrementing. Our application can choose 1 of 4 generations strategies:
1) AUTO
2) TABLE
3) SEQUENCE
4) IDENTITY
If not strategy is specified then AUTO is assumed
What is strategy = GenerationType.IDENTITY actually doing?
When we specify the generation strategy as GenerationType.IDENTITY we are telling the persistence provider(hibernate) to let the database handle the auto incrementing of the id. If you were to use postgres as an underling database and specified the strategy as IDENTITY, hibernate would execute this:
create table users (
id bigserial not null,
primary key (id)
)
Notice that they type of the id is bigserial, what is bigserial? As per the postgres documentation, bigserial is a large autoincrementing integer.
Conclusion
By specifying:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
you have told the underlying persistence layer to use the id field as a unique identifier within the database. Also told the persistence layer to let the database handle the auto incrementing of the id with GenerationType.IDENTITY.
In very simple words, we want to tell our Database (DB) what strategy to use to generate primary keys.
Now, primary keys must be different for every different row so there must be some strategy that will tell the DB on how to differentiate one row from another.
GenerationType lets us define that strategy.
Here #GeneratedValue(stratergy=GenerationType.IDENTITY) is telling our DB to store primary key in the identity column which is a default column in SQL for default auto incremented primary key generation.
I making a sample application with Hibernate. My requirement is that there is no primary key on the table. I had to do only select query from application. I know there should be a primary key, but the table I am referring has been made without it.
It has about 50k records. So, modifying the table to add ID column does not see viable option.
Can it possible?
Hibernate requires that entity tables have primary keys. End of story.
50k records is simply not that many when you're talking about a database.
My advice: add an autoincrement integer PK column to the table. You'll be surprised at how fast it is.
So, modifying the table to add ID column does not see viable option.
Why's that? Do you just mean, because it already has fifty thousand records? Trust me, that's really not very many.
Even if the table doesn't have a surrogate key, and doesn't have a primary-key constraint, and even if you're not willing to alter the table, it still probably has some sort of candidate key — that is, some set of columns that are never null, and whose values uniquely identify a record. Even without altering the table to enforce their uniqueness and non-nullity, you can tell Hibernate that those columns form a composite ID.
I have found solution for tables without primary key and null as values. It will work on oracle DB. Maybe something similar exists for other DBs.
You should create new primary key in the POJO class:
#Id
#Column(name="id")
private Integer id;
and use createNativeQuery like this
getEntityManager().createNativeQuery("select rownum as id, .....
The native query will generate primary key and you will get unique results.
Not with Hibernate. It requires a primary key.
Using DB2 with a table without primary keys the following works:
Define the primary key field in the Entity similar to:
#Id
#Column(name="id")
private Integer id;
For Select Query add:
String selectQuery = "select ROW_NUMBER() OVER () id, ..."
I haven't tested with updates to the entity but selecting the info from the DB works fine
Need to put #Index even when I marked with #Column(unique=true) ?
I have a property that will be used frequently to retrieve the entity and wanted to make it an index column on the database. So this property is already marked with #Column(unique=true), do I need to put #Index?
thanks
Most databases do implement UNIQUE constraints using a UNIQUE INDEX, but they aren't required to and a UNIQUE constraint does not necessarily give you the benefits of an index. In theory, a constraint would not be considered by the query planner whereas an index would be.
That said, in the particular case of MySQL, it seems that a UNIQUE constraint and a UNIQUE INDEX are "synonymous".
But you should confirm that by checking the query plan.
I presume you are using mysql as the question is tagged with mysql
If you are using annotations and something like this
#Column(unique = true)
Then this gets converted to the following DDL by hibernate
unique (user_id)
When you query the mysql db and do
show index from
It would show user_id as an indexed field, so the answer is #unique is enough for the field to be indexed
HTH
What strategy is good for migrating a hibernate class from a sequence based integer primary key to a GUID primary key while retaining the old keys for backward compatibility?
We have an extensive class hierarchy (using the joined-subclass model) where the base class has a Long primary key generated from a sequence in the DB.
We are working on transitioning to a GUID primary key, but wish to retain the old primary key (both in old and newly created content) for legacy apps. While the implementation seems fairly straightforward (change the primary key to GUID, add an interceptor to populate the content ID on new content), what pitfalls might I want to watch out for?
Are you sure you want to do this?
I understand wanting GUIDs, but do you really want them to be your database PKs. Some informal testing I did showed that there was about a 10-15% hit in using a GUID PK for joins / searches vs an integer PK. I would suggest you try out some tests with GUIDs on your current population and see what the performance hit is. It may be better to just add a uniquely indexed GUID column to your tables and leave the PKs as they are.
I can't think of a pretty solution but,
I would create another field to hold the GUIDs and auto generate IDs for any records that currently exist and go from there. It will smell a bit but it's better than trying to store incompatible types in the same field if you ask me.
Stupid bugs like "we know that PK is GUID so it's length is always that many"...