Spring JPA force save - spring

I have basic question but I cannot find the response for it.
As I see each time I change entity I have to save it.
But is there some way to enable autosave?

If the methods you update the entity is marked with #Transactional annotation, you don't need to call save or update methods.
Note that, the entity must be fetched from DB to be maintained. If the entity has already created, you still need to use save method.

Related

Spring Data problem - derived delete doesn't work

I have a spring boot application (based off spring-boot-starter-data-jpa. I have an absolute minimum of configuration going on, and only a single table and entity.
I'm using CrudRepository<Long, MyEntity> with a couple of findBy methods which all work. And I have a derived deleteBy method - which doesn't work. The signature is simply:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
Long deleteBySystemId(String systemId);
// findBy methods left out
}
The entity is simple, too:
#Entity #Table(name="MyEntityTable")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MyEntityPID")
private Long MyEntityPID;
#Column(name="SystemId")
private String systemId;
#Column(name="PersonIdentifier")
private String personIdentifier;
// Getters and setters here, also hashCode & equals.
}
The reason the deleteBy method isn't working is because it seems to only issue a "select" statement to the database, which selects all the MyEntity rows which has a SystemId with the value I specify. Using my mysql global log I have captured the actual, physical sql and issued it manually on the database, and verified that it returns a large number of rows.
So Spring, or rather Hibernate, is trying to select the rows it has to delete, but it never actually issues a DELETE FROM statement.
According to a note on Baeldung this select statement is normal, in the sense that Hibernate will first select all rows that it intends to delete, then issue delete statements for each of them.
Does anyone know why this derived deleteBy method would not be working? I have #TransactionManagementEnabled on my #Configuration, and the method calling is #Transactional. The mysql log shows that spring sets autocommit=0 so it seems like transactions are properly enabled.
I have worked around this issue by manually annotating the derived delete method this way:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
#Modifying
#Query("DELETE FROM MyEntity m where m.systemId=:systemId")
Long deleteBySystemId(#Param("systemId") String systemId);
// findBy methods left out
}
This works. Including transactions. But this just shouldn't have to be, I shouldn't need to add that Query annotation.
Here is a person who has the exact same problem as I do. However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
Oh, for reference I'm using Spring Boot 2.2.9.
tl;dr
It's all in the reference documentation. That's the way JPA works. (Me rubbing hands washing.)
Details
The two methods do two different things: Long deleteBySystemId(String systemId); loads the entity by the given constraints and ends up issuing EntityManager.delete(…) which the persistence provider is about to delay until transaction commits. I.e. code following that call is not guaranteed that the changes have already been synced to the database. That in turn is due to JPA allowing its implementations to actually do just that. Unfortunately that's nothing Spring Data can fix on top of that. (More rubbing, more washing, plus a bit of soap.)
The reference documentation justifies that behavior with the need for the EntityManager (again a JPA abstraction, not something Spring Data has anything to do with) to trigger lifecycle events like #PreDelete etc. which users expect to fire.
The second method declaring a modifying query manually is declaring a query to be executed in the database, which means that entity lifecycles do not fire as the entities do not get materialized upfront.
However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
There's detailed explanation why it works the way it works in the comments to the ticket. There are solutions provided even. Workarounds and suggestions to bring this up with the part of the stack that has control over this behavior. (Shuts faucet, reaches for a towel.)

Spring Boot: check if a given String is unique in the database

In Spring Boot, I need to check if a random String is unique. I suppose that a good way is to use an Entity with an unique column, and than:
if the String exists in the repository, adds it to the repository and return it.
That involses some code that may fail in a multithread environment, because while a thread is checking if a given String exists in the database, another thread can add it to the database at the same time.
Could you give me some hits to solve this problem? Thank you.
You can use pessimistic locking to solve that.
Spring has a #lock annotation with a lock type pessimistic, that might serve your needs. Otherwise you can implement your application in a way where it locks the entity before querying and releases the lock afterwards.
I would start from here: https://en.wikipedia.org/wiki/Record_locking
You can enable transaction locks on query Methods with #Lock annotation.
#Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Person> findById(Long PersonId);

Should we perform db operation using #PathVariable in Spring?

GET should be used for viewing something, without changing it, while POST should be used for changing something. For example, a search page should use GET, while a form that changes your password should use POST.
So in Spring we have #PathVariable annotation and we can access it in our controller.
My questions is:
1)Should we use path variable in our controller to perform any DB operation like delete or update as it seems clearly in the URL.If yes then it can be a hole in our application that anyone can make that request again.
I know that we can use #RequestParam with POST method in spring as well as with GET method but I just want to know that if it is okay to use PathVariable to change our database.
It is not a matter of #RequestParam, #RequestBody, or #PathVariable. You should check for correct user ROLES to do the database operation as long as these three methods can be automated using tools. Pure case of logic!
There is no matter if you use #PathVariable or #RequestParam.
I think the important part you have to think about is, that you can use hidden input fields. And they can be catched with the #RequestParam annotation.
So I would say it depends on what you want to show in your url and what you want to "hide".

Symfony 2 Validate Proxy Entity

So I have an entity with annotation asserts and I call validate() manually on the entity, everything works as expected.
However sometimes a process before this hydrates the same entity through a relationship. Then later when I fetch the entity normally I get a proxy of that entity (which is initialised), I believe Doctrine does this automatically for performance reasons.
However when I then come to validate it, because the class is a proxy the annotation asserts don't get picked up.
Should validate() know how to validate a proxy by loading the asserts from it's base class it proxies from? I assume not since it isn't.
So my question is, is there any way to turn a proxy entity into it's normal entity class (I don't mean hydrate/initialise it because it already is).
Or is there a way to force Doctrine to re-fetch the entity from the database and not return me an initialised proxy?
Ah-ha I had to call $em->clear() after requesting the related entity to make sure I got a none-proxy entity the next time round.

How can I test that a JPA save actually saves data?

I am using JPA with Spring and saving an entity in a test. In the process of writing a test to validate that an entity's relationship with another entity is correctly set up, I have come across a problem that I come across frequently. I have a test method (set to rollback) that:
Creates entity
Saves entity
Flushes
Retrieves entity
Validates entity
The problem is that when I look at the Hibernate logs, I only see a single insert to the database where I'd expect to see an insert and then a select.
I know this is because Hibernate's trying to save me some time and knows that it's got the entity with the ID I'm trying to retrieve but that bypasses an important step: I want to make sure that the entity actually made it to the database and looks like what I thought it should. What's the best way to deal with this so I can test that the entity is actually in the database?
Note: I assume this involves somehow detaching the entity or telling Hibernate to clear its cache but I'm not sure how to do that when all I have access to is a JpaRepository object.
Some code:
public interface UserRepository extends JpaRepository<User, Long> {
//...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = JpaConfig.class, // JpaConfig just loads our config stuff
loader = AnnotationConfigContextLoader.class)
#TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
#Test
#Transactional
public void testRoles() {
User user = new User("name", "email#email.com");
// eventually more here to test entity-to-entity relationship
User savedUser = userRepository.save(user);
userRepository.flush();
savedUser = userRepository.findOne(savedUser.getId());
Assert.assertNotNull(savedUser);
// more validation here
}
}
You basically want to test Hibernate's functionality instead of your own code. My first suggestion: don't do it! It is already tested and validated many times.
If you really want to test it, there are a couple of options:
Execute a query (rather than a get. The query will get executed (you should see it in the log) and the result interpreted. The object you get back would still be the same object you saved, since that is in the session.
You can evict the object from the session and then get it again. If you use SessionFactory.getCurrentSession(), you'll get the same season that the repository is using. With that you can evict the object.
You have two strategies:
issue a native SQL query therefor bypassing any JPA cache.
ensure the persistence context is cleared before reloading.
For (1) you can change your tests to extend the following Spring class which, in addition to automatically beginning/rolling back a transaction at the start/end of each test, will give you access to a Spring JdbcTemplate you can use to issue the native SQL.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.html
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/jdbc/core/simple/SimpleJdbcTemplate.html
For (2) you can clear the persistence context by doing the following (where the EntityManagerFactory is injected into your test:
EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory).clear();
See the following base test class which I normally use and demonstrates the above and also allows for populating the database with known data before each test (via DBUnit).
https://github.com/alanhay/spring-data-jpa-bootstrap/blob/master/src/test/java/uk/co/certait/spring/data/repository/AbstractBaseDatabaseTest.java
(In fact in the above I am actually creating a new JdbcTemplate by injecting a datasource. Can't remember why...)

Resources