У меня есть минимальное весеннее загрузочное приложение, состоящее из 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, который может сохранять сущность? У меня есть веские причины не создавать репозиторий (я пытался это сделать, и это работает).
С уважением Йенс
Это может быть полезно: stackoverflow.com/questions/17346679/…
Спасибо @MaximMarkov, который помог мне решить мою проблему, скоро опубликую рабочее решение.
@Gimby, есть ли правильный способ инициализации базы данных при запуске приложения (возможно, @EventListener)? Я помню, как некоторое время назад исследовал, как программно инициализировать/заполнить базу данных при запуске приложения, но не нашел ни одной передовой практики.




Насколько я понимаю, @PostConstruct вызывается при запуске приложения, когда мы хотим инициализировать bean-компоненты и конфигурации. Я думаю, что @PostConstruct — неподходящее место для этого.
Однако вы можете использовать @PersistenceContext в своем entityManger вместо его автоматического подключения.
Да, я видел несколько указателей на отсутствие транзакций в @PostConstruct/InitializingBean. Они у вас есть, если вы используете репозиторий вместо EntityManager, что, я думаю, меня и смутило. Я пробовал использовать @PersistenceContext и не заметил никакой разницы в поведении.
Итак, перепробовав множество разных вещей, я думаю, что нашел рабочее решение, в котором инициализация выполняется в обработчике 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
Управление транзакциями Spring — это простой способ. Однако это не делает транзакцию доступной в методе PostConstruct. Попытка инициализировать данные таким методом просто неправильный способ. Таким методом вы инициализируете bean-компонент, а не базу данных.