Сохранение объекта JPA с использованием простого EntityManager при весенней загрузке в @PostConstruct

У меня есть минимальное весеннее загрузочное приложение, состоящее из 3 классов: Entity, компонента, который пытается заполнить базу данных в @PostConstruct, и класса приложения. Ничего больше.

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

@Component
@Transactional
public class Initializer {
    @Autowired
    EntityManager em;

    @PostConstruct
    public void populate() {
        em.persist(new MyEntity());
    }
}

@Entity
public class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int id;
}

Когда я запускаю приложение, я получаю javax.persistence.TransactionRequiredException: нет EntityManager с фактической транзакцией, доступной для текущего потока, - не может надежно обработать вызов «постоянно».

Я не единственный, кто когда-либо получал эту ошибку, и я прочитал много сообщений, но не нашел волшебного решения.

Если я автоматически подключаю EntityMananagerFactory и вместо этого делаю:

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new MyEntity());
em.getTransaction().commit();
em.close();

Оно работает. Вопрос: есть ли более простой способ (поместить правильную аннотацию в нужное место) для получения EntityManager, который может сохранять сущность? У меня есть веские причины не создавать репозиторий (я пытался это сделать, и это работает).

С уважением Йенс

Управление транзакциями Spring — это простой способ. Однако это не делает транзакцию доступной в методе PostConstruct. Попытка инициализировать данные таким методом просто неправильный способ. Таким методом вы инициализируете bean-компонент, а не базу данных.

Gimby 17.06.2019 17:11

Это может быть полезно: stackoverflow.com/questions/17346679/…

Maxim Markov 17.06.2019 17:40

Спасибо @MaximMarkov, который помог мне решить мою проблему, скоро опубликую рабочее решение.

Jens Møller 18.06.2019 09:52

@Gimby, есть ли правильный способ инициализации базы данных при запуске приложения (возможно, @EventListener)? Я помню, как некоторое время назад исследовал, как программно инициализировать/заполнить базу данных при запуске приложения, но не нашел ни одной передовой практики.

Jens Møller 18.06.2019 11:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
1 110
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Насколько я понимаю, @PostConstruct вызывается при запуске приложения, когда мы хотим инициализировать bean-компоненты и конфигурации. Я думаю, что @PostConstruct — неподходящее место для этого.

Однако вы можете использовать @PersistenceContext в своем entityManger вместо его автоматического подключения.

Да, я видел несколько указателей на отсутствие транзакций в @PostConstruct/InitializingBean. Они у вас есть, если вы используете репозиторий вместо EntityManager, что, я думаю, меня и смутило. Я пробовал использовать @PersistenceContext и не заметил никакой разницы в поведении.

Jens Møller 18.06.2019 09:16
Ответ принят как подходящий

Итак, перепробовав множество разных вещей, я думаю, что нашел рабочее решение, в котором инициализация выполняется в обработчике ApplicationReadyEvent, а не в методе @PostConstruct:

@Component
public class Initializer {

    @PersistenceContext
    EntityManager em;

    @EventListener(ApplicationReadyEvent.class)
    @Transactional
    public void init() {
        em.persist(new MyEntity());
    }
}

Рабочий пример: https://github.com/djarnis73/spring-boot-db-init-with-jpa-entity-manager

Другие вопросы по теме