如何解决在Spring-Batch的上下文中持久化实体jpa的问题,该实体是一个联接表,带有一些额外的字段
读取两个表,对其进行修改并将其写回到外部数据库(socio_db)的批处理是微服务演示项目的一部分。 Spring-Batch的数据库更新按计划运行(每个24小时),仅修改两个字段(布尔值和枚举)。
(要修改的表格属于类似于Facebook的应用程序,社交在此注册并添加其他社交作为关联的社交):
在显示代码和错误消息之前:
- 我已经使用其自己的数据源配置了批处理元数据数据库(socio_batch_meta_data_db),因此它与作为目标数据库的socio_db分开了。
- 我将不包括有关两个DB字段的逻辑,因为它与SocioAssociatedSocio类的读取器-处理程序-写入器流程无关;
- 仅查找三个相关实体(Socio,SocioAssociatedSocio,SocioAssociatedSocioId)的代码。
错误消息:
2020-08-19 10:51:25,141 ERROR [restartedMain] org.springframework.batch.core.step.AbstractStep: Encountered an error executing step associatedsociodbsociowriteStep in job batchdbsociowriteJob
java.lang.IllegalArgumentException: Unable to invoke method: [public void com.artsgard.sociodbbatch.readers.AssociatedSocioReader.before(org.springframework.batch.core.StepExecution)] on object: [com.artsgard.sociodbbatch.readers.AssociatedSocioReader@9e233ce] with arguments: [[StepExecution: id=1178,version=1,name=associatedsociodbsociowriteStep,status=STARTED,exitStatus=EXECUTING,readCount=0,filterCount=0,writeCount=0 readSkipCount=0,writeSkipCount=0,processSkipCount=0,commitCount=0,rollbackCount=0,exitDescription=]]
at org.springframework.batch.support.SimpleMethodInvoker.invokeMethod(SimpleMethodInvoker.java:112)
at org.springframework.batch.core.listener.MethodInvokerMethodInterceptor.invoke(MethodInvokerMethodInterceptor.java:69)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy111.beforeStep(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.batch.support.SimpleMethodInvoker.invokeMethod(SimpleMethodInvoker.java:108)
... 39 common frames omitted
Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:281)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:103)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
Caused by: org.postgresql.util.PSQLException: ERROR: no existe la columna socioassoc0_.associatedsocioid
Hint: Probablemente quiera hacer referencia a la columna «socioassoc0_.associated_socio_id».
Position: 8
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2497)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2233)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:310)
Pom和属性文件:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.artsgard</groupId>
<artifactId>sociodbbatch</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
app.datasource.batch.url=jdbc:postgresql://localhost:5432/socio_batch_meta_data_db
app.datasource.batch.driverClassName=org.postgresql.Driver
app.datasource.batch.username=postgres
app.datasource.batch.password=Candita123
app.datasource.db.url=jdbc:postgresql://localhost:5432/socio_db
app.datasource.db.driverClassName=org.postgresql.Driver
app.datasource.db.username=postgres
app.datasource.db.password=Candita123
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.batch.initialize-schema=always
#86400000
batch.delay= 5000
spring.jpa.hibernate.ddl-auto=none
spring.datasource.initialization-mode=never
spring.datasource.initialize=false
数据库和JobRepositoy配置:
@Configuration
public class BatchDbMetaDataRepoConfig {
@Primary
@Bean(name = "batchDataSourceProperties")
@ConfigurationProperties("app.datasource.batch")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "batchDataSource")
@ConfigurationProperties("app.datasource.batch.hikari")
public DataSource dataSource(@Qualifier("batchDataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.artsgard.sociodbbatch.repository","com.artsgard.sociodbbatch.config" },entityManagerFactoryRef = "dbEntityManagerFactory",transactionManagerRef = "dbTransactionManager")
public class BatchDbRepoConfig {
@Bean(name = "dbDataSourceProperties")
@ConfigurationProperties("app.datasource.db")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "dbDataSource")
@ConfigurationProperties("app.datasource.db.hikari")
public DataSource dataSource(@Qualifier("dbDataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}
@Bean(name = "dbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,@Qualifier("dbDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.artsgard.sociodbbatch.model")
.persistenceUnit("db")
.build();
}
@Bean(name = "dbTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("dbEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
public class JobRepositoryConfig extends DefaultBatchConfigurer {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
@Qualifier("batchDataSource")
private DataSource datasource;
@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean();
factoryBean.setDatabaseType(DatabaseType.POSTGRES.getProductName());
factoryBean.setTablePrefix("BATCH_");
factoryBean.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
factoryBean.setDataSource(datasource);
factoryBean.setTransactionManager(transactionManager);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}
三个实体:
@Data
@NoArgsConstructor
@Entity
@Table(name = "socio") //,catalog = "socio_db"),schema = "socio_db"),schema = "socio_db")
public class SocioModel implements Serializable { // UserDetails
public SocioModel(Long id,String username,String password,String firstName,String lastName,String email,Boolean active,List<LanguageModel> socioLanguages,List<AddressModel> socioAddresses) {
this.id = id;
this.password = password;
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.active = active;
this.socioLanguages = socioLanguages;
this.socioAddresses = socioAddresses;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Column(name = "username",nullable = false,unique = true)
private String username;
@NotNull
@Column(name = "password",nullable = false)
private String password;
@Column(name = "first_name",nullable = true)
private String firstName;
@NotNull
@Column(name = "last_name",nullable = true)
private String lastName;
@NotNull
@Column(name = "email",unique = true)
private String email;
@NotNull
@Column(name = "register_date",nullable = false)
private Timestamp registerDate;
@NotNull
@Column(name = "last_checkin_date",nullable = false)
private Timestamp lastCheckinDate;
@NotNull
@Column(name = "active",nullable = false)
private Boolean active;
@JsonIgnore
@OneToMany(targetEntity=SocioAssociatedSocio.class,mappedBy="socio")
private List<SocioAssociatedSocio> socios;
@JsonIgnore
@OneToMany(targetEntity=SocioAssociatedSocio.class,mappedBy="socio") //associatedSocio
private List<SocioAssociatedSocio> associatedSocios;
@ManyToMany
@JoinTable(name = "socio_role",joinColumns = @JoinColumn(name = "socio_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<RoleModel> socioRoles;
@NotNull
@ManyToMany()
@JoinTable(name = "socio_language",inverseJoinColumns = @JoinColumn(name = "language_id"))
private List<LanguageModel> socioLanguages;
@OneToMany(mappedBy = "socio",cascade = CascadeType.REMOVE)
private List<AddressModel> socioAddresses;
}
@Entity(name = "SocioAssociatedSocio")
@Table(name = "socio_associated_socio") //,schema = "socio_db")
@IdClass(SocioAssociatedSocioId.class)
public class SocioAssociatedSocio implements Serializable {
private SocioAssociatedSocio() { }
public SocioAssociatedSocio(Long socioId,Long associatedSocioId,SocioModel socio,SocioModel associatedSocio,AssociatedSocioState associatedSocioState,Timestamp associatedSocioDate) {
this.socioId = socioId;
this.associatedSocioId = associatedSocioId;
this.socio = socio;
this.associatedSocio = associatedSocio;
this.associatedSocioState = associatedSocioState;
this.associatedSocioDate = associatedSocioDate;
}
@Id
private Long socioId;
@Id
private Long associatedSocioId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "socioId",updatable = false,insertable = false,referencedColumnName = "id")
private SocioModel socio;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "associatedSocioId",referencedColumnName = "id")
private SocioModel associatedSocio;
public enum AssociatedSocioState {
PENDING,EXPIRED,ACCEPTED,DENIED
}
@Column(name = "associated_socio_state",length = 100)
@Enumerated(EnumType.STRING)
private AssociatedSocioState associatedSocioState;
@Column(name = "associated_socio_date",nullable = true)
private Timestamp associatedSocioDate;
public long getSocioId() {
return socioId;
}
public void setSocioId(long socioId) {
this.socioId = socioId;
}
public long getAssociatedSocioId() {
return associatedSocioId;
}
public void setAssociatedSocioId(long associatedSocioId) {
this.associatedSocioId = associatedSocioId;
}
public SocioModel getSocio() {
return socio;
}
public void setSocio(SocioModel socio) {
this.socio = socio;
}
public SocioModel getAssociatedSocio() {
return associatedSocio;
}
public void setAssociatedSocio(SocioModel associatedSocio) {
this.associatedSocio = associatedSocio;
}
public AssociatedSocioState getAssociatedSocioState() {
return associatedSocioState;
}
public void setAssociatedSocioState(AssociatedSocioState associatedSocioState) {
this.associatedSocioState = associatedSocioState;
}
public Timestamp getAssociatedSocioDate() {
return associatedSocioDate;
}
public void setAssociatedSocioDate(Timestamp associatedSocioDate) {
this.associatedSocioDate = associatedSocioDate;
}
}
public class SocioAssociatedSocioId implements Serializable {
private SocioAssociatedSocioId() { }
private Long socioId;
private Long associatedSocioId;
@Override
public int hashCode() {
return (int) (socioId + associatedSocioId);
}
@Override
public boolean equals(Object object) {
if (object instanceof SocioAssociatedSocioId) {
SocioAssociatedSocioId otherId = (SocioAssociatedSocioId) object;
return (otherId.socioId == this.socioId)
&& (otherId.associatedSocioId == this.associatedSocioId);
}
return false;
}
}
批处理流
@Configuration
@EnableBatchProcessing
public class BatchFlowConfig {
@Autowired
@Qualifier("dbTransactionManager")
private PlatformTransactionManager transactionManager;
@Autowired
private JobBuilderFactory jobBuilders;
@Autowired
private StepBuilderFactory stepBuilders;
@Autowired
private JobRepository jobRepository;
@Autowired
private SocioProcessor socioProcessor;
@Autowired
private SocioReader socioReader;
@Autowired
private SocioWriter socioWriter;
@Autowired
private AssociatedSocioProcessor associatedProcessor;
@Autowired
private AssociatedSocioReader associatedReader;
@Autowired
private AssociatedSocioWriter associatedWriter;
@Bean(name = "sociojob")
public Job userDbJob() throws Exception {
return jobBuilders.get("batchdbsociowriteJob")
.repository(jobRepository)
.start(socioStep())
.next(associatedSocioStep())
.build();
}
@Bean
public Step socioStep() throws Exception {
return stepBuilders.get("sociobatchdbsociowriteStep")
.<SocioModel,SocioModel>chunk(20)
.reader(socioReader)
.processor(socioProcessor)
.writer(socioWriter)
.transactionManager(transactionManager)
.build();
}
@Bean
public Step associatedSocioStep() throws Exception {
return stepBuilders.get("associatedsociodbsociowriteStep")
.<SocioAssociatedSocio,SocioAssociatedSocio>chunk(20)
.reader(associatedReader)
.processor(associatedProcessor)
.writer(associatedWriter)
.transactionManager(transactionManager)
.build();
}
}
阅读器:
@Component
public class AssociatedSocioReader implements ItemReader<SocioAssociatedSocio> {
@Autowired
private AssociatedSocioRepository repo;
private Iterator<SocioAssociatedSocio> associatedSocioIterator;
@BeforeStep
public void before(StepExecution stepExecution) {
associatedSocioIterator = repo.findAll().iterator();
}
@Override
public SocioAssociatedSocio read() {
if (associatedSocioIterator != null && associatedSocioIterator.hasNext()) {
return associatedSocioIterator.next();
} else {
return null;
}
}
}
处理器:
@Component
public class AssociatedSocioProcessor implements ItemProcessor<SocioAssociatedSocio,SocioAssociatedSocio> {
@Autowired
private AssociatedSocioRepository repo;
@Override
public SocioAssociatedSocio process(SocioAssociatedSocio associated) throws Exception {
associated.setAssociatedSocioState(SocioAssociatedSocio.AssociatedSocioState.EXPIRED);
// logic not displayed
return associated;
}
}
作家:
@Component
public class AssociatedSocioWriter implements ItemWriter<SocioAssociatedSocio> {
@Autowired
private AssociatedSocioRepository repo;
@Override
public void write(List<? extends SocioAssociatedSocio> associated) throws Exception {
List<SocioAssociatedSocio> list = new ArrayList();
for (SocioAssociatedSocio scs: associated) {
if(scs != null) {
list.add(scs);
}
}
repo.saveAll(list);
}
}
最终评论: 有效的Socio读取器-处理器-写入器与上面显示的相同。问题在于,作为联接表(具有额外字段)的SocioAssociatedSocio与社会实体(常规实体)不同。
我相信交易管理和实体经理(工厂)存在一些问题。
任何帮助,欢迎改进评论。
感谢您对此的关注 威廉
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。