BigONotation
BigONotation

Reputation: 4538

Implementing Collection-oriented Repository (DDD) with mybatis

I want to implement a collection-based repository based on mybatis as the underlying persistence store. From "Implementing Domain-driven Design" (see chapter 12), it is indicated that the repository interface should mimic a Set:

For example:

interface FlightRepository{
   boolean add(Flight f);
   boolean remove(Flight f);
   // finder methods
   Flight findById(Integer id);
}

The mybatis implementation would be something along the following lines (assuming spring boot implementation):

@Mapper
interface FlightMapper{
      int createOrUpdate(Flight flight);
      Flight read(int id);
      int delete(Flight flight);
}

@Component
@Transactional
class FlightRepositoryImpl implements FlightRepository {
    @AutoWired FlightMapper mapper;

    boolean add(Flight f){
        int affectedRows = mapper.createOrUpdate(f);
        if(affectedRows == 1) return true 
        else return false;
    }

    boolean remove(Flight f){
        int affectedRows = mapper.delete(f);
        if (affectedRows == 1) return true
        else return false;
    }

    Flight findById(){
        return mapper.read(id);
    }

}

My question is how can I manage repository updates without having a saveChanges method:

Flight f = flightRepository.findById(1);

f.setDepartureTime(...);
f.setArrivalTime(...);

// flightRepository.saveChanges(); ??

One implementation option would be to have the repository make a copy of the original Flight object before returning it to the client. When the time comes to persist changes, it would compare its internal copy with the flight object returned to the client. If the internal copy does not match the object returned to the client, then the persistence store would be updated with the client's version (using flightMapper.createOrUpdate()).

However, how can I trigger this logic, without having the client call an explicit saveChanges() method? This would mean I would have to be able to somehow hook into the Repository object's lifetime using spring boot (in other words, trigger the updates before the transaction is being committed.

Upvotes: 1

Views: 890

Answers (2)

James Cauwelier
James Cauwelier

Reputation: 91

I know this is an old thread, but I was looking for the same and not finding an answer, so I leave mine here:

This is indeed not clearly answered in the book. Vaughn mentions the use of Hibernate to resolve this, but doesn't solve it in his own demonstration code. I think you're looking to find a way to have managed entities that are automatically saved without an explicit call.

The answer is found one level up, namely in your application services.

@Component
public class FlightService {
    private final FlightRepository repository;

    @Transactional
    public void changeTimes(
        String flightId, 
        String arrival, 
        String departure
    ){
        repository
            .findById(personId)
            .map((flight) -> {
                flight.setDepartureTime();
                flight.setArrivalTime();
                return flight;
            });
    }
}

Even though flight.save() is not called, it will still be persisted by way of the @Transactional annotation on the service method. So, in a nutshell, as I understand it, you need to embed all your mutating method calls inside a managed session, and the annotation is how to do that.

There are other mentions of this feature on StackOverflow:

Upvotes: 0

You can do something before commit by implementing TransactionSynchronization using TransactionSynchronizationAdapter for convenience:

@Bean
public class TransactionEventsListener extends TransactionSynchronizationAdapter {

    @Override
    public void beforeCompletion() {
        // do something before commit
    }
}

Upvotes: 1

Related Questions