HibernateTemplate save performs inserts but not updates - spring

I have a typical Spring / Hibernate setup. Here's my spring config:
<context:annotation-config />
<context:component-scan base-package="com.myco.myapp.modules" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sessionFactory"
...
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I have a BaseRepository:
#Transactional(propagation = Propagation.MANDATORY)
public final T save(final T entity) throws RepositoryException {
try {
getHibernateTemplate().save(entity);
return entity;
} catch (DataAccessException e) {
throw new EntityCouldNotBeSavedException(getPersistentClass(),
e);
}
}
And a Repository class that extends it:
#Repository
public class PersonRepositoryImpl extends BaseRepositoryImpl<Person, String>
And a Service:
#Service
public class PersonServiceImpl {
#Autowired
private PersonRepository _personRespository;
I call the following method, saveSomeStuff(), an when I insert using BaseRepository.save() it works perfectly. But when I try to update, it doesn't make the change:
#Override
#Transactional
public void saveSomeStuff() {
try {
Person existingPerson = _personRespository.findById("1");
existingPerson.setName("John");
_personRespository.save(existingPerson);
Person dbExistingPerson = _personRespository.findById("1");
// This prints "John".
System.out.println(dbExistingPerson.getName());
Person newPerson = new Person();
newPerson.setName("Jack");
_personRespository.save(newPerson);
} catch (RepositoryException e) {
e1.printStackTrace();
}
}
I thought I might have a transaccionality problem, but as I said, upon leaving the Service method the new Person is persisted in the database. In the log I see:
insert into person ...
However, the update I made is not persisted, and there is no error and no 'update' sql statement in the log. I thought the HibernateTemplate.save() method might be the problem but from within the saveSomeStuff() method, after loading the Person from the database, I do a System.out, and the Person loaded from the database has the updated name.
What am I missing here?

There is a separate method, saveOrUpdate(entity). You can use it if you don't want hibernate to generate id while saving.

Save method will Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.

Figured out the problem. If I had included my Entity class, someone probably would have seen it sooner than me.
#Entity
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
#Table(name = "PEOPLE")
public class Person {
...
}
Initially I was getting a cache error:
java.lang.UnsupportedOperationException: Can't write to a readonly object
The quick solution? Add the #Immutable annotation. But if you read the docs for it:
An immutable entity may not be updated by the application.
Updates to an immutable entity will be ignored, but no exception is thrown.
Which explains why 1) updates were being ignored and 2) no exceptions were being thrown.
So I got rid of the #Immutable annotation and changed Cache to:
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
And now everything works fine.
In summary: rtfm.

I had stumbled upon the same problem. The entity was getting inserted into the database, but while updating some of the columns where not getting updated and there were no errors in the log. After going through the entity class, I figured out that I had annotated some of my fields as below
#Column(name = "CREATED_DT", updatable = false)
private Date createdOn;
After removing the updatable attribute from the annotation, the update was working fine.

Related

JPA - Spanning a transaction over multiple JpaRepository method calls

I'm using SpringBoot 2.x with SpringData-JPA accessing the database via a CrudRepository.
Basically, I would like to call the CrudRepository's methods to update or persist the data. In one use case, I would like to delete older entries from the database (for the brevity of this example assume: delete all entries from the table) before I insert a new element.
In case persisting the new element fails for any reason, the delete operation shall be rolled back.
However, the main problem seems to be that new transactions are opened for every method called from the CrudRepository. Even though, a transaction was opened by the method from the calling service. I couldn't get the repository methods to use the existing transaction.
Getting transaction for [org.example.jpatrans.ChairUpdaterService.updateChairs]
Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteWithinGivenTransaction]
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteWithinGivenTransaction]
I've tried using different Propagation. (REQUIRED, SUPPORTED, MANDATORY) on different methods (service/repository) to no avail.
Changing the methods #Transactional annoation to #Transactional(propagation = Propagation.NESTED) sounded that this would just do that, but didn't help.
JpaDialect does not support savepoints - check your JPA provider's capabilities
Can I achieve the expected behaviour, not using an EntityManager directly?
I also would like to avoid to having to be using native queries as well.
Is there anything I have overlooked?
For demonstration purposes, I've created a very condensed example.
The complete example can be found at https://gitlab.com/cyc1ingsir/stackoverlow_jpa_transactions
Here are the main (even more simplified) details:
First I've got a very simple entity defined:
#Entity
#Table(name = "chair")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Chair {
// Not auto generating the id is on purpose
// for later testing with non unique keys
#Id
private int id;
#Column(name = "legs", nullable = false)
private Integer legs;
}
The connection to the database is made via the CrudRepository:
#Repository
public interface ChairRepository extends CrudRepository<Chair, Integer> {
}
This is being called from another bean (main methods here are updateChairs and doUpdate):
#Slf4j
#Service
#AllArgsConstructor
#Transactional
public class ChairUpdater {
ChairRepository repository;
/*
* Initialize the data store with some
* sample data
*/
public void initializeChairs() {
repository.deleteAll();
Chair chair4 = new Chair(1, 4);
Chair chair3 = new Chair(2, 3);
repository.save(chair4);
repository.save(chair3);
}
public void addChair(int id, Integer legCount) {
repository.save(new Chair(id, legCount));
}
/*
* Expected behaviour:
* when saving a given chair fails ->
* deleting all other is rolled back
*/
#Transactional
public void updateChairs(int id, Integer legCount) {
Chair chair = new Chair(id, legCount);
repository.deleteAll();
repository.save(chair);
}
}
The goal, I want to achieve is demonstrated by these two test cases:
#Slf4j
#RunWith(SpringRunner.class)
#DataJpaTest
#Import(ChairUpdater.class)
public class ChairUpdaterTest {
private static final int COUNT_AFTER_ROLLBACK = 3;
#Autowired
private ChairUpdater updater;
#Autowired
private ChairRepository repository;
#Before
public void setup() {
updater.initializeChairs();
}
#Test
public void positiveTest() throws UpdatingException {
updater.updateChairs(3, 10);
}
#Test
public void testRollingBack() {
// Trying to update with an invalid element
// to force rollback
try {
updater.updateChairs(3, null);
} catch (Exception e) {
LOGGER.info("Rolled back?", e);
}
// Adding a valid element after the rollback
// should succeed
updater.addChair(4, 10);
assertEquals(COUNT_AFTER_ROLLBACK, repository.findAll().spliterator().getExactSizeIfKnown());
}
}
Update:
It seems to work, if the repository is not extended from either CrudRepository or JpaRepository but from a plain Repository, definening all needed methods explicitly. For me, that seems to be a workaround rather than beeing a propper solution.
The question it boils down to seems to be: Is it possible to prevent SimpleJpaRepository from opening new transactions for every (predefined) method used from the repository interface? Or, if that is not possible, how to "force" the transaction manager to reuse the transaction, opened in the service to make a complete rollback possible?
Hi I found this documentation that looks will help you:
https://www.logicbig.com/tutorials/spring-framework/spring-data/transactions.html
Next an example take from the previous web site:
#Configuration
**#ComponentScan
#EnableTransactionManagement**
public class AppConfig {
....
}
Then we can use transactions like this:
#Service
public class MyExampleBean{
**#Transactional**
public void saveChanges() {
**repo.save(..);
repo.deleteById(..);**
.....
}
}
Yes this is possible. First alter the #Transactional annotation so that it includes rollBackFor = Exception.class.
/*
* Expected behaviour:
* when saving a given chair fails ->
* deleting all other is rolled back
*/
#Transactional(rollbackFor = Exception.class)
public void updateChairs(int id, Integer legCount) {
Chair chair = new Chair(id, legCount);
repository.deleteAll();
repository.save(chair);
}
This will cause the transaction to roll back for any exception and not just RuntimeException or Error.
Next you must add enableDefaultTransactions = false to #EnableJpaRepositories and put the annotation on one of your configuration classes if you hadn't already done so.
#Configuration
#EnableJpaRepositories(enableDefaultTransactions = false)
public class MyConfig{
}
This will cause all inherited jpa methods to stop creating a transaction by default whenever they're called. If you want custom jpa methods that you've defined yourself to also use the transaction of the calling service method, then you must make sure that you didn't annotate any of these custom methods with #Transactional. Because that would prompt them to start their own transactions as well.
Once you've done this all of the repository methods should be executed using the service method transaction only. You can test this by creating and using a custom update method that is annotated with #Modifying. For more on testing please see my answer in this SO thread. Spring opens a new transaction for each JpaRepository method that is called within an #Transactional annotated method

Mixing <mvc:annotation-driven conversion-service with #Autowired dependency coming back as null

I have a spring mvc 3.0 web application and am using a conversion-service as follows...
<bean id="applicationConversionService"
class="com.myer.reporting.converter.ApplicationConversionServiceFactoryBean"/>
<context:component-scan base-package="com.myer.reporting.controller" />
<context:component-scan base-package="com.myer.reporting.manager" />
<context:component-scan base-package="com.myer.reporting.dao"/>
<context:component-scan base-package="com.myer.reporting.dao.mapper"/>
<mvc:annotation-driven conversion-service="applicationConversionService"/>
In my controllers when i use the conversion service it all works fine.
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService; '
But for the first time I am now trying to use the applicationConversionService in my mapper using the same method but the applicationConversionService is always coming back as null.
package com.myer.reporting.dao.mapper;
public class ImportHistoryRowMapper implements RowMapper<ImportHistory> {
private static final String BUSINESS_DATE = "businessdate";
private static final String TOTAL_TRAN_COUNT = "total_tran_count";
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService;
public ImportHistory mapRow(ResultSet rs, int rowNum) throws SQLException {
Date businessDate = rs.getDate(BUSINESS_DATE);
Integer totalTranCount = rs.getInt(TOTAL_TRAN_COUNT);
ImportHistory importHistory = new ImportHistory();
importHistory.setBusinessDate(businessDate);
importHistory.setTransactionCount(totalTranCount);
// status
TrafficLightStatus status =
applicationConversionService.
getImportThresholdToStatusConverter()
.convert(totalTranCount);
importHistory.setStatus(status);
return importHistory;
}
}
I tried to add #Component to the top of the class but that doesn't seem to work either. I think I am lacking some understanding of the #Autowired annotation and therefore can't get this to work.
Can someone give me some advice on this please.
thanks
You need to make the ImportHistoryRowMapper spring managed for this to work.
I believe your dao class making the reference to this mapper class is singleton. For every request (i.e., call to method getJdbcTemplate().query ( sql, object array, new instance of ImportHistoryRowMapper);), you need to create new instance of ImportHistoryRowMapper i.e., it should be prototype.
You can inject prototype bean into singleton in 2 ways:
1) Lookup Method injection
2) Scoped proxies
Take a look at this.

Why hibernate flushes on select queries (EmptyInterceptor)?

I would like to understand a counter intuitive Hibernate behaviour I am seeing. I always thought that "flush" meant that hibernate had a data structure in-memory that has to be written to the DB. This is not what I am seeing.
I have created the following Interceptor:
public class FeedInterceptor extends EmptyInterceptor
{
#Override
public void postFlush(Iterator entities)
{
System.out.println("postFlush");
}
}
Registered it in my ApplicationContext
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="entityInterceptor">
<bean class="interceptor.FeedInterceptor"/>
</property>
</bean>
But, strange enough, I see "postFlush" written to the console for every row retrieved from the DB from my DAO:
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Feed feed");
query.list();
Why is that?
Let's assume Hibernate wouldn't flush the session, then you could have the following situation:
Person p = new Person();
p.setName("Pomario");
dao.create(p);
Person pomario = dao.findPersonByName("Pomario")
//pomario is null?
When finding a person by name, hibernate issues a select statement to the database. If it doesn't send the previous statements first, then the returned result might not be consistent with changes that have been done previously in the session, here the dababase hasn't received the create statement yet, so it returns an empty result set.

How to use transaction advisors with annotations?

supose I have the following example:
#Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class MyServiceImpl implements MyService {
...
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public TransactionResponse addMyEntity(MyEntity e) throws SQLException{
...
}
...
}
And in my applicationContext:
<tx:annotation-driven transaction-manager="txManager" />
Ok, now I want to add an interceptor after the rollback of the transaction if an SQLException is thrown. How can I do this?
Thanks in advance
EDIT
I'll try to clarify what I'm trying to do:
I have a WS, that persists some information on a DB and returns a response (an isOk boolean and an errorMessage if something went wrong)
Whenever an exception is risen, I need to:
Rollback the transaction
Build the response with the error message and return it to the client.
It's like a try/catch around spring's transaction proxy
Use the order attribute of tx:annotation-driven, and of the interceptor you will be using. Thus you specify which one runs before the other. See the AOP AspectJ advice ordering section

Spring #Transactional wrapping 2 methods

I'm a Spring newby. I use the #Transactional annotation for my dao methods:
#Transactional
public Person getById(long id) {
return new Person(jdbcTemplate.queryForMap(...));
}
#Transactional
public void save(Person person) {
jdbcTemplate.update(...);
}
and I've set up the transaction manager like this:
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
The problem is that when my client code calls dao.save(..) and then dao.getById(4) these happen in two separate transactions. How is it possible to wrap those 2 calls in the same database transaction? Ideally without doing it in a programmatic way.
thanks
It is bad practice to put transactional attributes in DAO layer. Also, I am not sure why do you require transaction for getById method. Even if you want to use transaction then you need to specify propagation behaviour as REQUIRES_NEW for save and getById method.
#Transactional(propagation = REQUIRES_NEW, readOnly = false)
public Person saveAndGetById(Person person, long id) {
save(person);
return getById(id);
}
#Transactional(propagation = REQUIRED)
public Person getById(long id) {
return new Person(jdbcTemplate.queryForMap(...));
}
#Transactional(propagation = REQUIRED, readOnly = false)
public void save(Person person) {
jdbcTemplate.update(...);
}
However, the best thing would be to have the "save" method return an ID, because it is hard to know beforehand which ID the Person will have once persisted.
Good practice in this case would be marking service method which invokes both these DAO methods as #Transactional. The case was clearly discussed here.

Resources