JPA attribute converter ustawia encje na dirty

1

Hej,

Taki przypadek. Custom type w encji - enum. Do tego JPA Coverter. Przy CrudRepository save, Hibernate robi Insert i Update. Jeśli nie użyję Convertera robi tylko Insert. Czyli w jakiś sposób Converter ustawia encję na dirty po Insercie. Problem - dodatkowy update po zapisie encji.

Jak sobie z tym poradzić?

/x

0

Pokaż kod convertera czy podałeś mu że jest mutable na false

0
@Converter(autoApply = true)
public class EventConverter implements AttributeConverter<Event, String> {

    @Override
    public String convertToDatabaseColumnt(Event attribute) {
        return attribute.toString();
    }
    
    @Override
    public Event convertToEntityAttribute(String dbData) {
        return Event.fromString(dbData);
    }
}
0

Dodam jeszcze Event:

public enum Event {
   TYPE_1("type 1"),
   TYPE_2("type 2");

   private String text;
   private Event(String text) {
       this.text = text;
   }

   public static fromString(String txt) {
      return Stream.of(values()).filter(type -> type.text.equalsIgnoreCase(txt)).findAny().orElseThrow(IllegalAgrumentException::new);
   }

   public String toString() {
      return text;
   }
}
1

Nie umiem powtórzyć Twojego błędu na środowisku testowym. Update powstaje mi tylko wtedy, gdy zmienię wartość enuma.

Zrobiłem na głupa zapis do bazy danych zawsze tego samego tekstu (tak, oczywiście słowo na 4 litery :) ). Mimo to zmiana enuma robi update, czyli hibernate sprawdza po prostu równość wszystkich pól. Może masz gdzieś indziej błąd. Zakładam, że nie używasz hibernate.ejb.use_class_enhancer=true.

0

Masz rację. Zrobiłem w domu testową wersję tylko z enumem i konwerterem i działa poprawnie - jeden INSERT. Więc to nie konwerter jest przyczyną problemów. W poniedziałek szukam dalej. Dam znać jak coś znajdę.

0

Hej,

Problem można zreprodukować, ale tylko (?) gdy używa się Convertera dla ZonedDateTime, czyli coś takiego:

@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Date> {

    @Override
    public Date convertToDatabaseColumn(ZonedDateTime dateTime) {
        return dateTime != null ? Date.from(dateTime.toInstant()) : null;
    }

    @Override
    public ZonedDateTime convertToEntityAttribute(Date date) {
        if (date != null) {
            Instant instant = Instant.ofEpochMilli(date.getTime());
            return ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
        }
        return null;
    }
}

Wtedy dostanę dodatkowy update po insercie encji zawierającej taki timestamp.

0

Ale przyczyną update'u jest prawdopodobnie nierówność obiektów i ten konwerter teoretycznie nie ma znaczenia. Pokaż cały kod, który pozwala zreprodukować.

0

Ok,

Spring boot app:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Domain:

@Table(name = "aaa")
@Entity
@Setter
@Getter
public class Event {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "event_seq")
    @SequenceGenerator(name = "event_seq", sequenceName = "aaa_seq", allocationSize = 1)
    private long id;

    @Column(name = "startdate")
    private ZonedDateTime startDate;
}

repo

public interface EventRepository extends JpaRepository<Event, Long> {
}

converter

@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Date> {

    @Override
    public Date convertToDatabaseColumn(ZonedDateTime dateTime) {
        return dateTime != null ? Date.from(dateTime.toInstant()) : null;
    }

    @Override
    public ZonedDateTime convertToEntityAttribute(Date date) {
        if (date != null) {
            Instant instant = Instant.ofEpochMilli(date.getTime());
            return ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
        }
        return null;
    }
}

Controller

@RestController
public class DefaultController {

    @Autowired
    private EventRepository eventRepository;

    @GetMapping("/create")
    public void createEvent() {
        Event event = new Event();
        event.setId(0);
        event.setStartDate(ZonedDateTime.now());
        eventRepository.save(event);
    }

}

2

Hej,

Problem solved. Spring boot używa Hibernate 5.0.12.FINAL. W wersji 5.2.11.FINAL jest tylko jeden INSERT. Bez UPDATE'A. Czyli BUG hibernate'a.

Znalazłem coś podobnego:
https://hibernate.atlassian.net/browse/HHH-11581

Chociaż wygląda nieco inaczej.

Dziwne, że Spring boot nadal (przynajmniej w wersji 1.5.6.RELEASE używa takiego starego Hibernate'a.

1 użytkowników online, w tym zalogowanych: 0, gości: 1