0

I have implemented an MQTT event listener like this:

@Service
@Slf4j
@RequiredArgsConstructor
public class SoilHumidityEventListener {

  private final MqttClient mqttClient;

  private final ObjectMapper objectMapper;

  private final PlantService plantService;

  @Value("${mqtt.soil_humidity_topic}")
  private String soilHumidityTopic;

  @Transactional
  public void subscribe() throws MqttException {
    mqttClient.subscribe(soilHumidityTopic, this::messageArrived);
  }

  @Transactional
  public void messageArrived(String topic, MqttMessage message) {
    try {
      SoilHumidityDto soilHumidityDto =
          objectMapper.readValue(message.toString(), SoilHumidityDto.class);
      Plant plant = plantService.findById(soilHumidityDto.getPlantId());
      SoilHumidity soilHumidity = SoilHumidity.fromDto(soilHumidityDto);
      soilHumidity.setPlant(plant);
      plant.getSoilHumidityList().add(soilHumidity);
      plantService.save(plant);

    } catch (Exception e) {
      log.error("Error parsing and saving soil humidity message: {}", e.getMessage());
    }
  }
}

The method plantService.findById(...) is also marked with @Transactional. the subscribe method in SoilHumidityEventListener is called in another service as bellow:

@Service
@RequiredArgsConstructor
@Slf4j
public class EventSubscriberService {

  private final SoilHumidityEventListener soilHumidityEventListener; 

  @PostConstruct
  @Transactional
  public void initialize() {
    try {
      soilHumidityEventListener.subscribe();      
    } catch (MqttException e) {
      log.error(e.getMessage());     
    }
  }
}

The problem is that there is no active transaction in the messageArrived() method. As a result I get a LazyInitializationException when trying to update the soilHumidityList of the plant object. The quick fix to this problem is to load the soilHumidityList eagerly. But I want to avoid this. Is there a way to keep the transaction open when handling MQTT messages?

MehdiB
  • 870
  • 12
  • 34
  • Does this answer your question? [Spring @Transaction method call by the method within the same class, does not work?](https://stackoverflow.com/questions/3423972/spring-transaction-method-call-by-the-method-within-the-same-class-does-not-wo) – crizzis Jun 18 '23 at 17:59
  • unfortunately not. I moved the logic in messageArrived method inside the subscribe method. and there is still no active transactions inside this method. – MehdiB Jun 18 '23 at 18:15
  • What do you mean you moved the logic inside the subscribe method? Did you just inline `messageArrived` into an anonymous lambda? – crizzis Jun 18 '23 at 18:28
  • 1
    Whether inlined or not, the handler will be executed on a different thread, and transactions don't span across threads. You need to move your handler method to a **different** bean – crizzis Jun 18 '23 at 18:29
  • Thanks, moving the method to another bean solved the problem. – MehdiB Jun 18 '23 at 19:48

0 Answers0