Spring JPA / Hibernate transaction force insert instead of update
I never did that before, but a little hack, would maybe do the job.
There is a Persistable
interface for the entities. It has a method boolean isNew()
that when implemented will be used to "assess" if the Entity is new or not in the database. Base on that decision, EntityManager should decide to call .merge()
or .persist()
on that entity, after You call .save()
from Repository
.
Going that way, if You implement isNew()
to always return true, the .persist()
should be called no mater what, and error should be thrown after.
Correct me If I'm wrong. Unfortunately I can't test it on a live code right now.
Documentation about Persistable
: http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Persistable.html
JPA firing INSERT before SELECT during an UPDATE?
This is the normal flushing behaviour of JPA. When you change an entity managed by JPA these changes get flushed to the database before a query gets executed.
You did change a User
entity by adding an Address
to it.
To fix that add the Address
only when it is fully constructed, i.e. it's AddressType
is set.
Note: I'm sure this is a duplicate, but I can't find it. But there is this closely related question: JPA auto flush before any query
why saveAll() always inserts data instead of update it?
Looks like I found the root of this behaviour.
Main App launcher look like:
@AllArgsConstructor
@SpringBootApplication
public class Application implements CommandLineRunner {
private final DataService dataService;
private final QrReaderServer qrReaderServer;
private final MonitoringService monitoringService;
@Override
public void run(String... args) {
dataService.fetchAndStoreData();
monitoringService.launchMonitoring();
qrReaderServer.launchServer();
}
All 3 steps have strict execution sequence. And the first one has to repeat for updating data locally if it is needed. Two other just servers which work with stored data only.
Where the first method look like:
@Scheduled(fixedDelay = 15_000)
public void fetchAndStoreData() {
log.debug("START_DATA_FETCH");
carParkService.fetchAndStoreData();
entityService.fetchAndStoreData();
assignmentService.fetchAndStoreData();
permissionService.fetchAndStoreData();
capacityService.fetchAndStoreData();
log.debug("END_DATA_FETCH");
}
Also, this execution is scheduled as well.
When the app starts it tried to execute this fetching twice:
2020-12-14 14:00:46.208 DEBUG 16656 --- [pool-3-thread-1] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH
2020-12-14 14:00:46.208 DEBUG 16656 --- [ restartedMain] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH
2 threads run at the same catch and store in parallel - trying to insert
data. (tables are recreated at every start).
All later fetches are fine, they are executed only by @Sceduled
thread.
If comment @Sceduled
- it will work fine without any Exceptions.
SOLUTION:
Added additional boolean property to service class:
@Getter
private static final AtomicBoolean ifDataNotFetched = new AtomicBoolean(true);
@Override
@Scheduled(fixedDelay = 15_000)
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public void fetchAndStoreData() {
ifDataNotFetched.set(true);
log.debug("START_DATA_FETCH");
// fetch and store data with `saveAll()`
log.debug("END_DATA_FETCH");
ifDataNotFetched.set(false);
}
And control the value after the application is started:
@Value("${sharepark.remote-data-fetch-timeout}")
private int dataFetchTimeout;
private static int fetchCounter;
@Override
public void run(String... args) {
waitRemoteDataStoring();
monitoringService.launchMonitoring();
qrReaderServer.launchServer();
}
private void waitRemoteDataStoring() {
do {
try {
if (fetchCounter == dataFetchTimeout) {
log.warn("Data fetch timeout reached: {}", dataFetchTimeout);
}
Thread.sleep(1_000);
++fetchCounter;
log.debug("{} Wait for data fetch one more second...", fetchCounter);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} while (DataServiceImpl.getIfDataNotFetched().get() && fetchCounter <= dataFetchTimeout);
}
force insert with spring data jpa
Forcing insert for on entity whose id is not null is not allowed in Spring Data JPA.
Normally an Id generator is used for a primary key that ensures ids generated are unique. (e.g. @GeneratedValue(strategy = GenerationType.AUTO))
This behavior in my opinion is correct. In an ORM, use of new operator indicates intention to create a new entity with an implicit requirement to assign it a unique id.
But if you wish to try it anyway you can look into a custom @GenericGenerator.
http://www.georgestragand.com/jpaseq.html
Spring crud repository - save() tries to insert new rows for many-to-one relationship in child property
this problem is happen when you don't declare CasCadeType
for Entity associated, Cascade contain several Type :
- CascadeType.PERSIST : cascade type presist means that save() or persist() operations cascade to related entities.
- CascadeType.MERGE : cascade type merge means that related entities are merged when the owning entity is merged.
- CascadeType.REFRESH : cascade type refresh does the same thing for the refresh() operation.
- CascadeType.REMOVE : cascade type remove removes all related entities association with this setting when the owning entity is deleted.
- CascadeType.DETACH : cascade type detach detaches all related entities if a “manual detach” occurs.
- CascadeType.ALL : cascade type all is shorthand for all of the above cascade operations.
the default value is Type.ALL
for your case you must change cascadeType
base on requirement.
Related Topics
How to Check If an Arraylist of Strings Contains Substrings of Another Arraylist of Strings
Generating Unique Random Numbers Effectively in Java
Android Room Persistent: Appdatabase_Impl Does Not Exist
How to Efficiently Read Multiple Json Files into a Dataframe or Javardd
Name a File in Java to Include Date and Time Stamp
Avoid Keycloak Default Login Page and Use Project Login Page
Rounded Swing Jbutton Using Java
How to Remove All Special Character in a String Except Dot and Comma
How to Exclude Classes from Test Coverage Using Java Annotations in Spring Boot
After Spring Boot 2.0 Migration: Jdbcurl Is Required With Driverclassname
Maven: Best Way of Linking Custom External Jar to My Project
How to Pass Json Object as a Pathvariable to Spring Controller
Failed to Process Import Candidates for Configuration Class
Round Off a Double While Maintaining the Trailing Zero
How to Exit an Android App Programmatically
How to Get Row Count Using Resultset in Java