Non-primary key business-id containing unique number from database - postgresql

I'm using JPA, Hibernate and Postgres. I'd like the code to be as solution neutral as possible, where JPA is a given.
My simplified entity looks like this:
#Entity
#Table(name = "example")
public class ExampleEntity {
#Id
#GeneratedValue
private UUID id;
private String businessId; //format is YYYY000001 where YYYY = current year. Current assumption: number is incrementing and reset every year, number is always filled up with leading 0 to make the key 10 digits
}
I always have a generated UUID as the primary key. The business-id shall only be set if a certain state has been reached and is therefore unrelated to when the entity has been created. I would like the database to take care of the incrementing number.
Preferably I'd like to solve this through JPA, but also see a "dirtier" solution where I fetch the sequence-id and generate the business-key in my logic.

Related

Spring Boot Hibernate Problem with generating entity ID using sequence or identity

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.

Hibernate keeps asking for hibernate_sequence when trying to persist DTO with generated UUID

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.

Generating a shorter ID with MongoDB and spring

I have an entity that is being saved with a MongoRepository. The class has a property Id annotated with #Id:
public class KCItem {
#Id
private String id;
When saved it generates a long id - 5bb390b88ba5b9248ad148ce. These ids will be part of URLs and I'd like them to be shorter (I don't expect to have more than 1 million total items). What would be a better way to either replace Id with a shorter value OR create a second attribute that is the short id?

GeneratedValue counter for Id resets every time that the server and client are executed

I'm working on a JavaEE application with EJB and JPA.
My Entities, are defined, for instance, like this:
#Entity
public class Utente implements Serializable {
#Id
#GeneratedValue
private int cod_utente;
private String nome_utente;
private String morada_utente;
#Temporal(TemporalType.DATE)
private GregorianCalendar dnasc_utente;
private int tel_utente;
private List<GregorianCalendar> agenda;
#OneToMany
#JoinColumn(nullable=true)
private List<Prescricao> lista_presc;
When I create entities Utente, the keys are generated sequentially starting from one. If I shut down the clients and server and execute them again, the "counter" of the key generator is reestablished. This results in an error because the application will try to create another Utente with primary key "1".
Can please someone help me solve this problem?
The code:
#Id
#GeneratedValue
private int cod_utente;
doesn't set a specific stategy to generate the values for the ID.
It is the same as this code:
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int cod_utente;
GenerationType.AUTO means that the persistence provider (in Glassfish the default persistence provider is EclipseLink) should choose an appropriate strategy for the database you are using. It looks like the persistence provider is choosing a strategy which is restarting the values after a server restart in your case.
There are different generation strategies, you can find some detailed information in the EclipseLink Wiki.
I guess your best bet is to use a database sequence (GenerationType.SEQUENCE) to generate the ID values.
Example:
Create a database sequence named GEN_SEQUENCE (if you let the persistence provider generate your tables I guess you can also let it create the sequence somehow but this example will show how to do it manually), you should look for information on how to do that in the database you are using (probably something like CREATE SEQUENCE gen_sequence;). Change your code to this:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq_gen")
#SequenceGenerator(name = "my_seq_gen", sequenceName = "GEN_SEQUENCE")
private int cod_utente;
You can also use the same sequence for different classes.
Update:
For the #SequenceGenerator you can set an allocationSize, this value is the amount of sequence values which get reserved. The default value is 50. When you have a sequence which starts at 0, the first time a value is requested from the sequence, the sequence allocates (and reserves) the values 0-49 (or 1-50). These values can be used by the persistence provider until all values have been used, then the next 50 values (50-99 or 51-100) will get allocated and reserved. The sequence remembers the current position, so that it doesn't give out the same range twice if it is used by multiple classes.
For the value of the allocationSize you can keep the default, but this may produce gaps in the IDs. If a sequence range (e.g. 0-49) gets allocated (reserved) and only one or some of the values are used (e.g. 0, 1 and 2) the other values of this range (3-49) will get "lost" on server restart. The next time a range of values is allocated it will be 50-99, so the next ID in your table will be 50.
Now you have the following IDs in your table: 0,1,2,50. Normally this shouldn't be a problem, but you can also set the allocationSize to a lower value or to 1 to avoid creating such gaps.
See also:
what is the use of annotations #Id and #GeneratedValue(strategy = GenerationType.IDENTITY)? Why the generationtype is identity?
what is sequence (Database) ? When we need it?
The differences between GeneratedValue strategies

Design issue with modeling an availability matrix

I am trying to design a specific part of a domain model for a website.
The issue I have is representing a sort of availability matrix with days of the week (Day) and time slots (TimeSlot).
Possible values for Day are:
Monday
Tuesday
Wednesday
...
Possible values for TimeSlot are:
Before school
Morning
Noon
Afternoon
After school
Evening
Nighttime
Currently, I have a JPA entity with the two above enums (Day and TimeSlot) as fields:
#Entity
public class DayToTimeSlot {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private Day day;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private TimeSlot timeSlot;
}
I then populate a DayToTimeSlot reference table with all possible values/combinations.
And finally, I have an Advertisement entity that has a #ManyToMany java.util.Set of DayToTimeSlot instances as follows:
#ManyToMany
private Set<DayToTimeSlot> dayToTimeSlots;
This results in a advertisement_day_to_time_slots join table which can grow very quickly.
Is there not a better way to model this availability matrix?
I suppose you have only 7Days * 7 TimeSlots = 49 entries in the DayToTimeSlot table and then you only reference them in the Advertisement table.
To remain fully flexible I don't see any better ways to model the availability matrix. But your problem seems to be the association table. I would put myself the following questions: how quickly does it grow? Will you reach 1million in the next 5 years? To search in such 1 million-entries table is not such a big problem.
Independently of your answer to the last question you could think of caching the DayToTimeSlot table.
Besides, if you really do not want to search Advertisment by DayToTimeSlot, you could save the associations as a String field in Advertisment and load them when the getter getDayToTimeSlots() is called. E.g. On save of the Advertisment you itrate through the whole set of DayToTimeSlot, ad build a string of form : "MONDAY_BEFORE_SCHOOL, TUESDAY_NOON,..." and save it. On loading of an Advertisment you process the string in order to create the Set. Of course, in this case you will delete the #ManyToMany reference.
But another feasible solution would be the following (if for every week day you have a single entry of TimeSlot):
If you want to search for Advertisment only by day, or if you don't mind writing a bit more complex queries, then you could to add 7 new fields: mondayAvailability, tuesdayAvailability, to the Advertisment entity, save the data there and get rid of the #ManyToMany relationship..