Optimistic locking - session based locking - spring

I have a question related to optimistic locking in Spring/Hibernate actually.
I have following scenario with typical REST application with SQL database.
User A enters page and reads data - version 0 of entity - GET request
User B enters page and reads data - version 0 of entity - GET request
User A saves data - version 1 of entity - PUT request
User B wants to save data (PUT request), but I should see optimistic lock exception
Now my question:
Where hibernate saves data about entity version? I understand the situation when everything is in the same transaction:
Load data
Someone changed entity in the different transaction
Save data
But in my situation version will vanish GET and PUT are in totally different transaction/threads etc.
In my opinion I should save somewhere version loaded by the user to have correlation between GET and PUT requests e.g. in HTTP session or just return version in the response and then send that version in the PUT request.
Can it be done in the better way? Like out of the box?

JPA/Hibernate have #Version column in entity definition to check optimistic or pessimistic lockings. JPA/Hibernate saves versions in the table. For example you have Country table in db:
CREATE TABLE country (
id BIGINT NOT NULL AUTO_INCREMENT,
version BIGINT DEFAULT 0,
...
);
And Country entity:
#Entity
#Table(name = "country")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Version
#Column(name = "version")
private Long version;
...
}
If you update the country entity instances with optimistic lock conflicts, you get OptimisticLockException in JPA. You don't need to manage the versions, JPA/Hibernate checks the versions of entity instances for you.
Update for different transactions:
In different transactions you can get OptimisticLockException too because JPA/Hibernate checks in every update the version columns with database table. As long as you save your changes (commit), another changes of entity versions will be checked, doesn't matter whether in the same transaction or different transaction. Better you can manage your transactions with #Transactional annotation in Spring framework.

Related

Is using #Entity annotation in Spring boot JPA creates a table?

BACKGROUND
I am new to developing API in spring boot. I have this project wherein it is connected to an Oracle DB and PostgreSQL. The Oracle DB already have an existing tables and I need to fetch some data from multiple tables and send it back as a response. The Postgres DB is where I store the users data and other some data that doesn't need to be stored in the Oracle DB. I am currently using native queries.
The Account is an entity wherein I just marked one of the columns as the #Id (It is actually not an Id but it is unique for all accounts):
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Account {
#Id
private String sampleProperty1;
private String sampleProperty2;
private String sampleProperty3;
private String sampleProperty4;
private String sampleProperty5;
}
Now I have a repository interface:
public interface IAccountRepository extends JpaRepository<Account, String> {
#Query(value = "SELECT * FROM TABLE(SAMPLE_PACKAGE.SAMPLE_FUNC_GETACCOUNTS(?1))", nativeQuery = true)
List<Account> getAllAccountsByClientNumber(String clientNumber);
}
I was able to fetch the data and JPA mapped the columns automatically to my entity. Basically I am creating an Entity (Spring boot) for the data in my Oracle DB where the only purpose of it is to map the data and send it back to the user.
QUESTIONS
Will this approach create a table in my Oracle DB? I checked the Oracle DB and there is no table. But I'm worried it might somehow create a table of ACCOUNT in the oracle DB when it is on production. If this might happen, how can I prevent it?
This scenario also applies to other functionality like fetching transaction history, creating transaction, updating the Account data that are all in the Oracle DB. Am I doing it just right or there is a better option?
Is creating an Entity without a corresponding table have a drawback in Spring boot?
Note
I know you might say that I should just use the Oracle DB and create entities based on the existing tables. But in the future of the API, it will not have a connection with the Oracle DB. I already tried using projections it was also good, but I still needed to create a Response model and mapped it then send it back to user and creating a unit tests using the projection is pretty long and it sucks haha
You can set the following property:
spring.jpa.hibernate.ddl-auto=update
update will update your database if database tables are already created and will create if database tables are not created.

Spring Data Hibernate Idenity strategy not generating values from 1, can someone explain me how this works?

I am trying to insert a record from my spring application, but whenever I try to insert a new record, ID generated is 6 digit value even if there are no records present in the database. Can someone help me on why is it working this way.
My Entity class code is:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer customerId; ... rest of code
Database table had one record previously and when I added a new record, generated value was as shown.(P.s. my database had records previously which I deleted and added a new record(rohan) manually using mysql and when another record was added, this value was generated.)
mysql database snapshot

Spring data. How can I update an entity, having the id modified?

Spring data jpa + postgres.
Have an entity
class Entity {
#Id
#GeneratedValue// generator from here https://stackoverflow.com/questions/60989691/how-to-manually-set-a-value-for-generatedvalue/61007375#61007375
private int id;
private String value;
}
And what I wish to do is to UPDATE an existing entity, setting a different id (be it a bad practice or not) value.
By default it of course is treated as a new entity and is attempted to be INSERTed.
Going by the flow of #Modifying seems to do the job right, but currently struggling to find if I can pass the whole entity instead of pinpointing every field:
update Entity e set e.id=?1, e.value=?2 where...
to
update Entity e set e=?1
So the questions here would be:
1. Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
2. If not, is there a way to provide the full entity to be consumed by the #Query?
Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
If you are using GenerationType.AUTO strategy for #GeneratedValue (AUTO is the default strategy), then you can set id of Entity to null before calling save. It will insert a new record with rest of the fields being the same as original. id of new record will be generated automatically by database engine.
If you are using GenerationType.AUTO strategy for #GeneratedValue
If not, is there a way to provide the full entity to be consumed by the #Query?
You can chose not use #Query. A far simpler approach can be using default save method provided by JPA repositories to directly pass an Entity object.

Postgresql and Spring Roo Binding serial type for ID

I am having trouble using the Postgresql serial type in Spring Roo. What I want is to have the an auto-incrementing id column which will work with the auto generated entity classes in Roo.
The Postgresql sequences, which are generated with the default way of doing things in Spring Roo, work fine within the spring application. But sometimes I have to manually insert rows in the database using sql. (the sequences dont seem to work properly when I do an INSERT INTO... statement). If I could use serial type, then manual INSERTS are easy.
For example I have an office entity and and employee entity. There is a many-to-one relationship between employees and offices.
Here is my class for the Office entity.
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class Office {
#Id
#Column(name="officeid", columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long officeid;
/**
* Office Name
*/
#NotNull
#Size(max = 100)
private String name;
}
While this does work when my test inserts an office record, it fails when an employee record is inserted since the officeid foreign key value is null. (I guess it needs to flush between the office insert and the employee insert, but the auto-generate tests dont seem to do that.)
So what is the proper annotations to use to tell Roo (and hibernate/jpa) to use the serial data type, and also to work properly with inserts and relationships within the spring application?
Roo generates default JPA annotations, you must customize and setup them as needed. Note Roo guarantees your changes won't be modified.

self referencing object in JPA

I am trying to save a SystemUser entity in JPA. I also want to save certain things like who created the SystemUser and who last modified the system User as well.
#ManyToOne(targetEntity = SystemUser.class)
#JoinColumn
private SystemUser userWhoCreated;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(iso=ISO.DATE_TIME)
private Date timeCreated;
#ManyToOne(targetEntity = SystemUser.class)
#JoinColumn
private SystemUser userWhoLastModified;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(iso=ISO.DATE_TIME)
private Date timeLastModified;
I also want to ensure that these values are not null when persisted. So If I use the NotNull JPA annotation, that is easily solved (along with reference to another entity)
The problem description is simple, I cannot save rootuser without having rootuser in the system if I am to use a DataLoader class to persist JPA entity. Every other later user can be easily persisted with userWhoModified as the "systemuser" , but systemuser it's self cannot be added in this scheme.
Is there a way so persist this first system user (I am thinking with SQL). This is a typical bootstrap (chicken or the egg) problem i suppose.
Have you tried rootUser.setUserWhoLastModified(rootUser) ?

Resources