英文:
"Unable to locate persister" Error with Spring batch + Hibernate ORM
问题
I'm trying to create a system with Spring batch using Hibernate ORM but can't solve this Error
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:652) ~[spring-beans-6.0.2.jar:6.0.2]
...
These are my setting files.
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
...
hibernate-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>
...
PersonTest1.java
package com.example.batchprocessing.model;
...
HibernateUtil.java
package com.example.batchprocessing.util;
...
BatchConfiguration.java
package com.example.batchprocessing;
...
This is how my project looks like
英文:
I'm trying to create a system with Spring batch using Hibernate ORM but can't solve this Error
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]
This is how my project looks like
These are my setting files.
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>
hibernate-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
Notthing special here.
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
I just copied this from internet so hope there is no problem here.
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;
}
}
BatchConfiguration.java
reader() is where I am trying to write to DB.
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[]
}
答案1
得分: 1
This line in HibernateUtil is problematic because applySettings
will only apply properties but no mappings:
HibernateUtil中的这行代码存在问题,因为`applySettings`只会应用属性而不会应用映射:
```java
Judging from the implementation of Configuration.configure(String resource)
, you need to call StandardServiceRegistryBuilder.configure(String resource)
to apply the mappings declared in hibernate-annotation.cfg.xml:
根据Configuration.configure(String resource)
的实现,你需要调用StandardServiceRegistryBuilder.configure(String resource)
来应用在hibernate-annotation.cfg.xml中声明的映射:
Or, if you don't actually have a reason to create the ServiceRegistry yourself, instead call configuration.buildSessionFactory()
and let the Configuration class use the internal StandardServiceRegistryBuilder.
或者,如果你实际上没有理由自己创建ServiceRegistry,可以调用configuration.buildSessionFactory()
,让Configuration类使用内部的StandardServiceRegistryBuilder。
英文:
This line in HibernateUtil is problematic because applySettings
will only apply properties but no mappings:
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.build();
Judging from the implementation of Configuration.configure(String resource)
, you need to call StandardServiceRegistryBuilder.configure(String resource)
to apply the mappings declared in hibernate-annotation.cfg.xml:
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.configure("hibernate-annotation.cfg.xml")
.build();
Or, if you don't actually have a reason to create the ServiceRegistry yourself, instead call configuration.buildSessionFactory()
and let the Configuration class use the internal StandardServiceRegistryBuilder.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论