Я пытаюсь создать систему с пакетом Spring, используя Hibernate ORM, но не могу решить эту ошибку.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reader' defined in class path resource [com/example/batchprocessing/BatchConfiguration.class]: Failed to instantiate [org.springframework.batch.item.file.FlatFileItemReader]: Factory method 'reader' threw exception with message: Unable to locate persister: com.example.batchprocessing.model.PersonTest1
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:652) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:488) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1324) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) ~[spring-context-6.0.2.jar:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.2.jar:6.0.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.0.jar:3.0.0]
at com.example.batchprocessing.BatchProcessingApplication.main(BatchProcessingApplication.java:12) ~[classes/:na]
Это мои файлы настроек.
Pom.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>batch-processing-complete</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>batch-processing-complete</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
спящий режим-annotation.cfg.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection properties - Driver, URL, user, password -->
<property name = "hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name = "hibernate.connection.url">jdbc:postgresql://localhost:5432/testdb</property>
<property name = "hibernate.connection.username">postgres</property>
<property name = "hibernate.connection.password">password</property>
<!-- org.hibernate.HibernateException: No CurrentSessionContext configured! -->
<property name = "hibernate.current_session_context_class">thread</property>
<!-- Mapping with model class containing annotations -->
<mapping class = "com.example.batchprocessing.model.PersonTest1" />
</session-factory>
</hibernate-configuration>
PersonTest1.java Здесь ничего особенного.
package com.example.batchprocessing.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "people")
public class PersonTest1 {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "person_id", nullable=false, unique=true, length=11)
private int personId;
@Column(name = "last_name")
private String lastName;
@Column(name = "first_name")
private String firstName;
public int getPersonId() {
return personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
HibernateUtil.java Я просто скопировал это из Интернета, так что надеюсь, что здесь нет проблем.
package com.example.batchprocessing.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.example.batchprocessing.JobCompletionNotificationListener;
public class HibernateUtil {
private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
//Annotation based configuration
private static SessionFactory sessionAnnotationFactory;
private static SessionFactory buildSessionAnnotationFactory() {
try {
Configuration configuration = new Configuration();
configuration.configure("hibernate-annotation.cfg.xml");
log.info("--- Hibernate Configuration loaded ---");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
log.info("--- Hibernate Annotation serviceRegistry created ---");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
} catch (Throwable ex) {
log.error("--- Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionAnnotationFactory() {
sessionAnnotationFactory = buildSessionAnnotationFactory();
return sessionAnnotationFactory;
}
}
Пакетная конфигурация.java reader () — это место, где я пытаюсь писать в БД.
package com.example.batchprocessing;
import javax.sql.DataSource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import com.example.batchprocessing.model.PersonTest1;
import com.example.batchprocessing.util.HibernateUtil;
@Configuration
public class BatchConfiguration {
private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
// tag::readerwriterprocessor[]
@Bean
public FlatFileItemReader<Person> reader() {
PersonTest1 personTest = new PersonTest1();
personTest.setLastName("John");
personTest.setFirstName("Wick");
// Get Session
log.info("--- Creating session ---");
SessionFactory sessionFactory = HibernateUtil.getSessionAnnotationFactory();
Session session = sessionFactory.getCurrentSession();
log.info("--- Starting transaction ---");
session.beginTransaction();
log.info("--- Saving model ---");
session.persist(personTest);
log.info("--- Commiting transaction ---");
session.getTransaction().commit();
log.info("--- Closing session ---");
sessionFactory.close();
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names(new String[]{"firstName", "lastName"})
.fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Person>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
.dataSource(dataSource)
.build();
}
// end::readerwriterprocessor[]
// tag::jobstep[]
@Bean
public Job importUserJob(JobRepository jobRepository,
JobCompletionNotificationListener listener, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1)
.end()
.build();
}
@Bean
public Step step1(JobRepository jobRepository,
PlatformTransactionManager transactionManager, JdbcBatchItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person> chunk(10, transactionManager)
.reader(reader())
.processor(processor())
.writer(writer)
.build();
}
// end::jobstep[]
}
Эта строка в HibernateUtil проблематична, потому что applySettings
будет применять только свойства, но не сопоставления:
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.build();
Судя по реализации Configuration.configure(String resource)
, вам нужно вызвать StandardServiceRegistryBuilder.configure(String resource)
, чтобы применить сопоставления, объявленные в hibernate-annotation.cfg.xml:
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.configure("hibernate-annotation.cfg.xml")
.build();
Или, если у вас на самом деле нет причин создавать ServiceRegistry самостоятельно, вместо этого вызовите configuration.buildSessionFactory()
и позвольте классу Configuration использовать внутренний StandardServiceRegistryBuilder.
Я попробовал первое из ваших решений, и оно сработало! Большое спасибо!