Postgresql: How #GeneratedValue works in postgresql? - postgresql

I am very new to postgresql. In playframework with Ebean, I have used mysql and auto generated value there was actually auto incremented in that case. The sequence I got was 1,2,3,4...
But in Postgresql, the generated value doesn't seem to have such property. It seems that for every session it is starting with a number larger that the previous session's number and then auto incremented. The sequence in this case, 1,21,41,42,61,81,101,102,103...
My code segment:
#Entity
public class Post extends Model{
#Id
#GeneratedValue
#Required
public int id;
I am using:
Playframework 2.3.4
Ebean
Postgres 9.3
I really don't know the reason. Is there any particular reason of such values? Is there any catch about this? Do I need to switch to auto increment? And if so, how do I do it?

K, so you're using AUTO strategy, as it's the default. I don't know much about Ebean, but with Hibernate, a sequence called hibernate_sequence is created.
This sequence would be shared by other entities, so that might be part of the reason for the gaps. Your ORM might also be pre-allocating sequence values for performance reasons.
Either way, I always use IDENTITY strategy with Postgres, even though Postgres doesn't use IDENTITY. It does however create a sequence per table, which is generally what you want.

Related

Spring Data JPA bulk identifier generation

I have an entity, for which apart from the primary key, an extra unique identifier should be generated:
#Entity
class MyEntity(
val otherId: String // <- this id is unique as well
) {
#Id
#Generated
var id: UUID // PK
}
otherId property value is derived from a postgres sequence value, by calling SELECT nextval(...) and adding a prefix string. When I do bulk inserts, I have to resort to a custom query defined in my JPA repository for the entity, which retrieves multiple sequence values at once, but I'd like to make this process automatic.
I tried to implement IdentifierGenerator interface, but the best I could achieve is a single SELECT nextval query was made for each new entity inserted, which is totally unacceptable in my case since batches can consist of hundreds of entities. Digging into the hibernate details didn't give me an answer how to do that either.
Is there a way to generate a number of ids via some callback/hook for multiple entities at once? Or I still have to do everything by hand?
There are hooks to implement this, see this article as an example: https://thorben-janssen.com/custom-sequence-based-idgenerator/
To improve performance, you will have to configure the increment size, which by default is 50. This means that it will increment the sequence by 50 and put these values in a pool from which the values are served for identity generation.

Setting JPA behavior for importing entities from tables

How can I tell JPA how to behave with different column types in my database when it tries to generate the entities from tables?
For example when I have a column like the following in my MySQL:
`deleted` tinyint(1) NOT NULL DEFAULT 0,
I want in the generated entity by JPA have boolean instead of byte, but what the JPA will generate is something like this:
#Column(nullable=false)
private byte deleted;
However I want to have something like this:
#Column(nullable=false)
#Type(type = "org.hibernate.type.NumericBooleanType")
private boolean deleted;
I think there must be a way that I tell JPA how to translate the column types in my tables in the entities in Java!?
I don't like to modify the entities by hand!
If you're asking how to configure the Eclipse wizard to map TINYINT onto boolean, the answer is you probably cannot.
Using Hibernate tools looks more promising, though. There's a hibernate.reveng.xml config file you can use to control type mapping.
As a side note:
I don't like to modify the entities by hand!
Note that reverse engineering tools in general lack the business knowledge required to generate a business model structure that is completely sensible. You will likely have to do some tweaking (e.g you likely won't get any #ManyToMany associations, even if they are the more natural solution domain-wise).
In the last page of that wizard we can define the expected type for each column. The interesting part is, eclipse stores somehow the selected types for each column and in the future when you try to regenerate the entities you don't need to do this step times to times!

JPA: generate non pk unique and random alphanumeric value

I want to uniquely identity an entity without using the primary key. So I thought about generating an unique and random value. Moreover, value must be easy to read / manually copy and is expected to be 6 or 7 characters long.
Design
My entity A:
public class A{
// ...
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
// ...
public String getValue(){
return value;
}
protected void setValue(String value){
this.value = value;
}
}
represented in the database by the table
CREATE TABLE IF NOT EXISTS schema.mytable{
-- ...
value TEXT NOT NULL DEFAULT generate_unique_value_for_mytable(),
-- ...
CONSTRAINT "un_value" UNIQUE (value),
-- ...
}
I thought letting the database handling this and then fetch the value...
Problem
With the current design, value is correctly generated in the database but when JPA fetches A entities, value field is empty.
I cannot remove insertable=false otherwise, it will hit against the NOT NULL constraint
If I remove insertable=false and I put some dummy data, the data overrides the value generated by generate_unique_value_for_mytable()
If I remove everything in the Column annotation, I can save the A entity but value is still empty
Ugly solution
I couldn't find a proof but it looks like having the database generating a value is a bad idea. I do have the same problem for a non-primary key field which is generated by a sequence: I cannot fetch the value from the database.
So my ugly solution is to decorate the create() method of the EJB responsible for A entities:
public class Aejb{
public void create(A entity){
// method kind of ensures randomness
String value = MyUtil.generateRandomValue();
A isThereAnyoneHere = findByValue(value);
while(isThereAnyoneHere != null){
String value = MyUtil.generateRandomValue();
isThereAnyoneHere = findByValue(value);
}
// unicity is ensured
entity.setValue(value);
em.persist(entity);
}
}
Questions
Can I fetch a non-primary key value generated by the database from a JPA entity? Value can be generated by a function or a sequence.
Is there a more elegant solution than my ugly workaround to provide an unique and random value?
Yes.You haven't mentioned your database, but it is possible for
Oracle to return the value inserted via triggers, and have
Eclipselink obtain this value in your model - see
https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_returninsert.htm
Set the value using a #PrePersist method that will get executed
before the entity is inserted, but if you are relying on one or more database queries, you will run into performance issues, as inserting a new A will be expensive. You might instead just insert the random value and deal with the occasional conflict, and pick some random that has less chance of overlaps, like a UUID.
If I understand correctly, #Generated annotation should do the trick. This annotation sets the value from database DEFAULT field value.
Example:
#Generated(GenerationTime.INSERT)
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
However there is a drawback: if you decide to set value of your field in Java, it would be overwritten by Hibernate using the result from DEFAULT in your database.
Self-answer to mark question as closed
Final solution
We finally went for a combination of
Stored procedures: the database will generate the value. The procedure also ensures that the value is unique across the table
Named queries: to fetch the generated value by the procedure. I did not use NamedStoredProcedures because we are using PostgreSQL and PostgreSQL JDBC driver did not support name parameters which raised some problems.
With this configuration, the EJB is sure to have at most one database call to fetch the requested value.
Response to other answers
Here is a summary of the other answers feedback for self-reference and next readers:
Oracle trigger: we're using PostgreSQL :(
UUID: We had the constraint of having our unique and random code human-readable. An end-user is assumed to be able to manually rewrite it. Consequently, we could not have a long String such as an UUID.
PrePersist: Other business actions take place after the code generation in the same transaction which means that those actions need to be redone in case of collision. I'm not very confident about managing JPA exception (transaction scope and so on) so I preferred not to play with it.
#Generated: This is a Hibernate specific feature. We're using EclipseLink
Database Trigger: If code were purely generated at database level, I encountered the same problems of not fetching the value: the value is properly generated as database level but the entity will have the value as null

JPA and PostgreSQL with GenerationType.IDENTITY

I have a question about Postgres and GenerationType.Identity vs Sequence
In this example...
#Id
#SequenceGenerator(name="mytable_id_seq",
sequenceName="mytable_id_seq",
allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="mytable_id_seq")
I understand that I am specifying a Postgres sequence to use via annotations.
However, I have an id column defined with the 'serial' type, I have read that I can simply use GenerationType.IDENTITY and it will automatically generate a db sequence and use it to auto increment.
If that's the case, I don't see an advantage to using the SEQUENCE annotations unless you are using an integer for an id or have some specific reason to use another sequence you have created. IDENTITY is alot less code and potentially makes it portable across databases.
Is there something I'm missing?
Thanks in advance for the feedback.
If you have a column of type SERIAL, it will be sufficient to annotate your id field with:
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
This is telling Hibernate that the database will be looking after the generation of the id column. How the database implements the auto-generation is vendor specific and can be considered "transparent" to Hibernate. Hibernate just needs to know that after the row is inserted, there will be an id value for that row that it can retrieve somehow.
If using GenerationType.SEQUENCE, you are telling Hibernate that the database is not automatically populating the id column. Instead, it is Hibernate's responsibility to get the next sequence value from the specified sequence and use that as the id value when inserting the row. So Hibernate is generating and inserting the id.
In the case of Postgres, it happens that defining a SERIAL column is implemented by creating a sequence and using it as a default column value. But it is the database that is populating the id field so using GenerationType.IDENTITY tells Hibernate that the database is handling id generation.
These references may help:
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators
https://www.postgresql.org/docs/8.1/static/datatype.html#DATATYPE-SERIAL
From "Pro JPA2" book:
"Another difference, hinted at earlier, between using IDENTITY and other id generation strategies is that the identifier will not be accessible until after the insert has occurred. Although no guarantee is made about the accessibility of the identifier before the transaction has completed, it is at least possible for other types of generation to eagerly allocate the identifier. But when using identity, it is the action of inserting that causes the identifier to be generated. It would be impossible for the identifier to be available before the entity is inserted into the database, and because insertion of entities is most often deferred until commit time, the identifier would not be available until after the transaction has been committed."
I think it can be helpful if you are using the same sequence for more than one table (for example you want a unique identifier for many types of bills) ... also If you want to keep track of the sequence away from the auto generated key
You can find here the solution of updating the PostgreSQL table creation accordingly, in order to work with the GenerationType.IDENTITY option.

Postgresql/openJPA (geronimo) Sequence Issue

I am having a weird problem with a sequence. Im using postgresql 9 with geronimo 2.2. I have created the sequence PLANTS_ID_SEQ inside the db environment and when I try to create a new entity I get an error in my logs (which comes from postegresql) that the relation PLANTS_ID_SEQ exists. It seems that it tries to create the sequence that is already created. This is the code from the entity bean:
#Id
#GeneratedValue(generator="PLANTS_SEQ",strategy=GenerationType.SEQUENCE) #SequenceGenerator(name="PLANTS_SEQ", sequenceName="PLANTS_ID_SEQ",allocationSize=1) #Column(name = "ID")
private Integer id;
Please notice that if I change the sequence name (eg sequenceName="MY_SEQ")then the code runs correctly but it creates in postgresql (and obviously uses) the MY_SEQ sequence. If anyone has a clue about this case please share.
Thanks George
If your table has a column of type SERIAL, then postgres will create the sequence for you and use it automatically on inserts.
The sequence it creates is named "tablename_id_seq"...
Probably you're trying to duplicate what postgres has already done, and create a duplicate sequence.
Solved:
Should add in persistence.xml the following property:
property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(Sequences=false)"
this way, the MappingTool of openjpa will not try to create the sequence again.