Question

We are using the spring framework with jpa and jms.

Most of our services involves DB persistence and JMS message throwing.

Here is an example:

@Override
@Transactional
public void createAccount(PlayerDTO playerDTO) {

    Player newPlayer  = new Player(playerDTO);

    playerRepository.save(newPlayer);

    produceJmsMessage(new PlayerCreatedEvent());
 }

Problem is, that jms is not part of our transactional process. (only jpaTransactionManager) and therefore whenever the last line executed the message is fired without the commit against the DB taking place.

Now imagine that one of the message consumers is trying to fetch the player in order to process some stuff but! when he fetches the player from the db it is not been committed yet!

That happens to us all the time in a wide variety of scenarios.

Now, of-course we can use JTA but the performance impact is unbearable, and moreover its seems to be really unpopular in regards to documentations and stuff,

So the question is:

How do developers mitigate this issue we described here? (without JPA).

Best,

Était-ce utile?

La solution

sessionTransacted property solved my problem, it actually waits until transaction is committed before firing the events. (this is set to OFF by default):

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="cachingConnectionFactory" />
    <property name="messageConverter" ref="eventConverter" />
    <property name="sessionTransacted" value="true"/>
</bean>

Autres conseils

You could wrap your transactional PlayerService - or whatever it is that is responsible for saving the player - with a facade. The PlayerService could just be responsible for saving the player transactionally - and the facade could be responsible for invoking the PlayerService and then generating the message. Splitting out the send operation from the PlayerService would ensure that the Player is committed before the message is generated. For example:

@Service
public class PlayerServiceImpl implements PlayerService {

    @Autowired
    private PlayerRepository playerRespository;

    @Override
    @Transactional
    public void savePlayer(PlayerDTO playerDTO) {
        Player newPlayer  = new Player(playerDTO);

        // Responsible for saving the player only
        playerRepository.save(newPlayer);
    }

    ...
}

And then that could be wrapped with some sort of facade.

@Service
public class PlayerFacade {

    @Autowired
    private PlayerService playerService;

    public void createAccount(PlayerDTO playerDTO) {
        playerService.savePlayer(playerDTO);

        // Generate the message after successful save.
        // The player would be committed at this point because
        // the savePlayer method (and thus transaction) has completed
        produceJmsMessage(new PlayerCreatedEvent());
    }
}

Client code would then interact with the PlayerFacade rather than the PlayerService directly - to ensure that both the player is saved and message sent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top