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.)
Related
I have a problem with spring data, when executing FindOne when I am updating a product, to compare the value of an attribute with the same one in the database. The FindOne process brings me the same object that I have in memory and not the one from the database, someone knows how I do to bring the one from the database, I know it's because of the hibernate cache, but I can't make it work in repository
#Override
public CuentaDetalle findOne(Long id) {
return cuentaDetalleRepository.getOne(id);
}
It can be done. You should detach the entity.
If everything is well set in your spring-boot project you can easily autowire EntityManager, and then later in your method, you can use entityManager.detach()
#Autowired
private EntityManager entityManager;
...
public someMethod(CuentaDetalle cuentaDetalleToDetach) {
entityManager.detach(cuentaDetalleToDetach);
Then later when using return cuentaDetalleRepository.getOne(id); where id is equal to the cuentaDetalleToDetach.id, the fresh veriosn from db will be returned.
Although I strongly advise using this approach carefully.
There are some drawbacks to detached objects, for example, you cannot use lazy fetch on collection properties (one-to-many).
Then the detached entity will not be saved at the end of the transaction. To save it you should explicitly use cuentaDetalleRepository.save(cuentaDetalleToDetach).
I will stop here, there is a lot to write on this topic.
But I hope, I did answer your question.
I have an entity "Job" with a boolean flag "suspended":
#Entity
#XmlRootElement(name = "Job")
#Where(clause = "deleted=0")
public class Job {
...
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private boolean suspended;
...
}
And a Spring CrudRepository (JPA Hibernate) is used for persistence:
#Repository
public interface JobRepository extends CrudRepository<Job, Integer>, JobStatusSupport {}
I need to update the "suspended" flag individually, without overwriting updates done to other fields in concurrent threads. So the natural thing to do seemed to be writing a method that only updates the "suspended" field:
public class JobRepositoryImpl implements JobStatusSupport {
private final static String SET_SUSPENDED = "UPDATE Job SET suspended = :suspended, modificationDate = :modificationDate WHERE id = :id";
#Override
public int setSuspended(int id, boolean suspended, Instant modificationDate) {
int updateCount = em.createQuery(SET_SUSPENDED)
.setParameter("suspended", suspended)
.setParameter("modificationDate", modificationDate)
.setParameter("id", id)
.executeUpdate();
return updateCount;
}
}
Now I have the following scenario in my code (shortened obviously, in reality this is spread out over several methods, but this example does reproduce the problem):
#Transactional
public void resumeJob(int id) {
Job jobA = jobRepository.findOne(Integer.valueOf(id));
// jobA.suspended == true
// let's set "suspended" to "false"
int updateCount = jobRepository.setSuspended(id, false, Instant.now());
// OK: updateCount is 1
Job jobB = jobRepository.findOne(Integer.valueOf(id));
// jobB.suspended == true ??? that was just set to "false, wasn't it?
}
Probably I am missing some basics about JPA/Hibernate. But still, this is extremely counterintuitive: Why is jobB.suspended still "true" although the update is successful and the data is read again "from the DB"? Why is the update of the individual field not visible within the transaction?
(As one would expect, after the transaction is complete, Job.suspended is "false" in the database and for subsequent reads.)
How would one go about this properly? How should I write code that updates individual fields so that JPA becomes aware of what was done? Do I have to look into "merge" for something as simple as this?
Being able to write our own SQL statements is crucial for our project. I am trying Spring Data JPA mainly to avoid the tedious work of writing tons of CRUD operations. But if I am already encountering such problems with this simple scenario, I am wondering whether we would not be better of using JdbcTemplate.
UPDATE: LEARNED SOMETHING ABOUT ORM
Man, was I clueless! I worked in projects where JPA was used before. But I never had to deal with it in detail (and I wonder if anybody else did).
The entire effort of writing an "update method" is futile! I have reduced this to the following:
#Transactional
public void resumeJob(int id) {
Job job = jobRepository.findOne(Integer.valueOf(id));
job.setSuspended(false);
job.setName("And Now for Something Completely Different.");
}
That's all! This updates the DB and the cache and God knows what. The #Transactional annotation alone is sufficient for persisting the changes. If the annotation is removed, the DB remains unchanged. So ORM is basically working against a cache (through "attached objects") that everybody sees. Then one hopes that people put #Transactional in the right places (not on private methods, for example...) and that the ORM machinery knows what it is doing (for example, not making cache updates visible outside of an open transaction).
Honestly, this seems a bit too magic for my taste. But now that I know what it is all about, I will give it a try. Writing gazillions of CRUD methods isn't very appealing, either.
Please comment if I got this wrong or if you have links with best practices. (I am starting to wonder if it wouldn't be best to immediately detach every object I get from the DB, defeating the entire purpose of ORM :-)
UPDATE: EntityManager#clear() Is Enough for a Quick Fix
This is definitely not the clever way to use ORM, but for the moment I can simply call clear() in the few update methods I have written. This invalidates the entire cache and the next read somewhere else in the transaction receives the updated data. Of course, the right way to do it, would be to simply modify the attached entity, i.e. "job.setSuspended(false);".
Calling flush() is not needed, probably it only becomes of interest when you want to minimize the risk of losing data in case of a system crash. I suppose that Hibernate does not immediately write completed transactions to disk?
It's counter-intuitive, but if you think about it, it's quite normal.
You load an entity with ID 3. Hibernate stores it in its session cache
You execute an update query. This query is almost a black box to Hibernate. It can't know which rows are affected by the changes, and you're not doing these changes by modifying the entities, but by modifying rows in the database directly. So the rows are modified, but the entity with ID 3 is left, untouched, in the session cache
You load the entity again, in the same session. So Hibernate just returns the instance that is already in the cache, and thus doesn't contain the changes.
If you want an updated entity, you have two solutions:
modify the database by modifying the entity, or
clear the cache after the update query has been made.
I am using Spring Data rest with Spring JPA. I have one Spring JPA derived delete query that should deletes the list of items but when I am executing it and noticing the console I found that it is executing select query instead that's very strange situation I have ever come across.
#RepositoryRestResource(collectionResourceRel="revision", path="revision")
interface RevisionRepository extends JpaRepository<Revision, Long> {
List<Revision> deleteByContentId(long contentId)
}
I have even tried using Long instead of List<Revision> doesn't work and also have tried removeByContentId it is also doesn't work either and keeps executing the select query instead delete query.
when I am running this method this is what I got on my console
Hibernate: select revision0_.id as id1_2_, revision0_.body as body2_2_, revision0_.content_id as content_3_2_, revision0_.content_type as content_4_2_, revision0_.date_created as date_cre5_2_, revision0_.file_name as file_nam6_2_, revision0_.folder_id as folder_i7_2_, revision0_.force_ssl as force_ss8_2_, revision0_.is_active as is_activ9_2_, revision0_.lookup as lookup10_2_, revision0_.meta_description as meta_de11_2_, revision0_.meta_keywords as meta_ke12_2_, revision0_.meta_title as meta_ti13_2_, revision0_.nav_item as nav_ite14_2_, revision0_.nav_order as nav_ord15_2_, revision0_.regions_objects as regions16_2_, revision0_.summary as summary17_2_, revision0_.title as title18_2_, revision0_.updated_by as updated19_2_, revision0_.user_id as user_id20_2_ from revisions revision0_ where revision0_.content_id=?
does anyone having any idea why it is behaving strangely?
You need to add #Modifying annotation to your delete method. You will also need to make sure it is executed within a transaction, so you might need to add #Transactional annotation too, if you invoke this method not in a transaction.
Please see an example:
#Modifying
#Transactional
int deleteByFieldName( Long fieldValue );
In latest Spring >=5 and Spring Boot >=2.
#Transactional
int deleteByFieldname( Long fieldValue );
Works fine.
Note 1:
#Modifiyng annotation has absolutely no effect in this. It only work for #Query annotation. Without #Query, it is simply ignored.
Note 2:
In naming convention, that depends on the configured NamingStrategy, the CamelCase might be interpreted and nested entity relation or "_" in field name. So "Fieldname" and "FieldName" mean very different things.
Note 3:
Derived delete queries like this have a really nasty n+1 side effect. They ALWAYS first issue select for the rows then isses delete each row/entity one by one with separate delete stement. No way around this, except using #Query() with manual delete statement. And using #Query then requires the #Modifying for delete query.
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...)
I'm new to using Spring with Neo4j and I have a question about #Autowire for a GraphRepository.
Most examples I've seen use one #Autowire per Controller, but I have two Nodes I need to modify at the same time when a particular method is called in the controller. Should I simply #Autowire the repositories for both nodes (eg per the code below)? Is there any impact if I do this in a second controller with the same repositories as well (so if I had a ChatSessionController which also #Autowired ChatMessageService and ChatSessionService)?
ChatMessageController.java
#Controller
public class ChatMessageController {
#Autowired
private ChatMessageService chatMessageService;
#Autowired
private ChatSessionService chatSessionService;
#RequestMapping(value = "/message/add/{chatSessionId}", method = RequestMethod.POST)
#ResponseBody
#Transactional
public void addMessage(#RequestBody ChatMessagePack chatMessagePack,
#PathVariable("chatSessionId") Long chatSessionId) {
ChatMessage chatMessage = new ChatMessage(chatMessagePack);
chatMessageService.save(chatMessage);
// TODO: Make some modifications to the ChatSession as well
}
}
Any help would be much appreciated! I've been googling and looking through Stackoverflow to understand this better but I haven't found anything yet. Any pointers in the right directions would be great.
Another underlying question is, should I be (and can I?) modifying other Nodes in a GraphRepository that handles a particular node? Eg Should my GraphRepository be able to modify my GraphRespository?
Thanks!
I'm not convinced that this is a SO question, it's not really a Neo4J or Spring question either, it is more about the architecture of your application. However assuming that you understand the negatives of class fan out, and how to use the #Transactional annotation to achieve what you want then the answer to your question is that it is just fine to have many Repositories (Neo4J or otherwise, autowired or otherwise) in your class and in as many classes as you want.
Neo4J transactions default to Isolation level READ_COMMITTED and if you need anything else, you need to add the guards/locks yourself. Nested transactions are consideredd tobe the same transaction. The Spring #Transactional annotation relies on proxies that you should be aware of as they have implications when calling methods from within the same class.
I would go through this tuotorial over at Spring Data and get your head around how real world vs domain vs node models differ, there will be cases where one repository impacts another node type but I would think it is often transparent to you (i.e adding relationships). You can do what you like in each repository (the generic nature of them is largely confined to all of the built in CRUD and queries derived from finder-method names (see documentation ) using the #Query annotation, and some queries have side effects, but largely you should avoid it.
As you start adding multiple repositories to multiple controllers I think that your code will begin to smell bad and that you should consider encapsulating this business logic off on its own somewhere, neatly unit tested. I also wouldn't tie myself to one controller per data object, it would be fine to have a single ChatController with a POST/chat/ to create a new session and POST /chat/{sessionId} to add a message. Intersting questions on Programmers:
How accurate is "Business logic should be in a service, not in a model?"
Best Practices for MVC Architecture
MVC Architecture — How many Controllers do I need?