Trouble with Generic Repository in Spring Data JPA [duplicate] - java

This question already has answers here:
Using generics in Spring Data JPA repositories
(3 answers)
Closed 4 years ago.
I'm trying to do some basic database operations using a generic repository in Spring Data JPA. However, I keep running into an IllegalArgumentException "Not an managed type: " + whatever class/interface I have my parameter extend.
GenericRepository.java
#Transactional
public interface GenericRepository<T> extends PagingAndSortingRepository<T, Integer> {
#Query("SELECT g "
+ "FROM " + "#{#entityName}" + " g "
+ "WHERE g.#{#entityName}Id = ?1"
)
public T findById(int Id);
For example, the above complains about class Object, whereas using GenericRepository (a marker interface I was trying to use) it'll complain about interface DatabaseDerived being not a managed type.
I'm simply using #SpringBootApplication for most of my setup, and the answers I've been able to find for this problem either definitely don't work in my case or I can't seem to figure out how to apply them to my setup or I just plain don't understand them. I would be grateful if someone could point me to a useful tutorial (couldn't find one on Google) or walk me through how to get this working with my setup.
Application.java
#SpringBootApplication
#ComponentScan("radius.hibernate")
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>annonymized</groupId>
<artifactId>radius.hibernate</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Adjuster.java
/**
* Adjuster generated by hbm2java
*/
#Entity(name = "adjuster")
#Table(name = "Adjuster", schema = "dbo", catalog = "annonymized")
public class Adjuster implements java.io.Serializable, DatabaseDerived {
private int adjusterId;
private EagleUsers eagleUsersBySupervisorId;
private EagleUsers eagleUsersByUserId;
private boolean isW2;
private boolean autoAssign;
private boolean active;
private boolean isRecordDeleted;
private Set<AdjusterSoftware> adjusterSoftwares = new HashSet<AdjusterSoftware>(0);
private Set<BankInvoiceCorrection> bankInvoiceCorrectionsForOldAdjusterId = new HashSet<BankInvoiceCorrection>(0);
private Set<BankAdjuster> bankAdjusters = new HashSet<BankAdjuster>(0);
private Set<SymbilityAdjuster> symbilityAdjusters = new HashSet<SymbilityAdjuster>(0);
private AdjusterCompensation adjusterCompensation;
private AdjusterContact adjusterContact;
private Set<AdjusterClaimType> adjusterClaimTypes = new HashSet<AdjusterClaimType>(0);
private Set<AdjusterClientExclude> adjusterClientExcludes = new HashSet<AdjusterClientExclude>(0);
private Set<AdjusterEquipment> adjusterEquipments = new HashSet<AdjusterEquipment>(0);
private Set<ClaimAdjuster> claimAdjusters = new HashSet<ClaimAdjuster>(0);
private Set<ClaimReport> claimReports = new HashSet<ClaimReport>(0);
private Set<Invoice> invoices = new HashSet<Invoice>(0);
private Set<LicenseAdjuster> licenseAdjusters = new HashSet<LicenseAdjuster>(0);
private Set<AdjusterClientContactExclude> adjusterClientContactExcludes = new HashSet<AdjusterClientContactExclude>(
0);
private Set<BankInvoiceCorrection> bankInvoiceCorrectionsForNewAdjusterId = new HashSet<BankInvoiceCorrection>(0);
private AdjusterAutoAssign adjusterAutoAssign;
private Set<AdjusterLossType> adjusterLossTypes = new HashSet<AdjusterLossType>(0);
public Adjuster() {
}
public Adjuster(int adjusterId, EagleUsers eagleUsersBySupervisorId, EagleUsers eagleUsersByUserId, boolean isW2,
boolean autoAssign, boolean active, boolean isRecordDeleted) {
this.adjusterId = adjusterId;
this.eagleUsersBySupervisorId = eagleUsersBySupervisorId;
this.eagleUsersByUserId = eagleUsersByUserId;
this.isW2 = isW2;
this.autoAssign = autoAssign;
this.active = active;
this.isRecordDeleted = isRecordDeleted;
}
public Adjuster(int adjusterId, EagleUsers eagleUsersBySupervisorId, EagleUsers eagleUsersByUserId, boolean isW2,
boolean autoAssign, boolean active, boolean isRecordDeleted, Set<AdjusterSoftware> adjusterSoftwares,
Set<BankInvoiceCorrection> bankInvoiceCorrectionsForOldAdjusterId, Set<BankAdjuster> bankAdjusters,
Set<SymbilityAdjuster> symbilityAdjusters, AdjusterCompensation adjusterCompensation,
AdjusterContact adjusterContact, Set<AdjusterClaimType> adjusterClaimTypes,
Set<AdjusterClientExclude> adjusterClientExcludes, Set<AdjusterEquipment> adjusterEquipments,
Set<ClaimAdjuster> claimAdjusters, Set<ClaimReport> claimReports, Set<Invoice> invoices,
Set<LicenseAdjuster> licenseAdjusters, Set<AdjusterClientContactExclude> adjusterClientContactExcludes,
Set<BankInvoiceCorrection> bankInvoiceCorrectionsForNewAdjusterId, AdjusterAutoAssign adjusterAutoAssign,
Set<AdjusterLossType> adjusterLossTypes) {
this.adjusterId = adjusterId;
this.eagleUsersBySupervisorId = eagleUsersBySupervisorId;
this.eagleUsersByUserId = eagleUsersByUserId;
this.isW2 = isW2;
this.autoAssign = autoAssign;
this.active = active;
this.isRecordDeleted = isRecordDeleted;
this.adjusterSoftwares = adjusterSoftwares;
this.bankInvoiceCorrectionsForOldAdjusterId = bankInvoiceCorrectionsForOldAdjusterId;
this.bankAdjusters = bankAdjusters;
this.symbilityAdjusters = symbilityAdjusters;
this.adjusterCompensation = adjusterCompensation;
this.adjusterContact = adjusterContact;
this.adjusterClaimTypes = adjusterClaimTypes;
this.adjusterClientExcludes = adjusterClientExcludes;
this.adjusterEquipments = adjusterEquipments;
this.claimAdjusters = claimAdjusters;
this.claimReports = claimReports;
this.invoices = invoices;
this.licenseAdjusters = licenseAdjusters;
this.adjusterClientContactExcludes = adjusterClientContactExcludes;
this.bankInvoiceCorrectionsForNewAdjusterId = bankInvoiceCorrectionsForNewAdjusterId;
this.adjusterAutoAssign = adjusterAutoAssign;
this.adjusterLossTypes = adjusterLossTypes;
}
#Id
#Column(name = "AdjusterId", unique = true, nullable = false)
public int getAdjusterId() {
return this.adjusterId;
}
public void setAdjusterId(int adjusterId) {
this.adjusterId = adjusterId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SupervisorId", nullable = false)
public EagleUsers getEagleUsersBySupervisorId() {
return this.eagleUsersBySupervisorId;
}
public void setEagleUsersBySupervisorId(EagleUsers eagleUsersBySupervisorId) {
this.eagleUsersBySupervisorId = eagleUsersBySupervisorId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "UserId", nullable = false)
public EagleUsers getEagleUsersByUserId() {
return this.eagleUsersByUserId;
}
public void setEagleUsersByUserId(EagleUsers eagleUsersByUserId) {
this.eagleUsersByUserId = eagleUsersByUserId;
}
#Column(name = "IsW2", nullable = false)
public boolean isIsW2() {
return this.isW2;
}
public void setIsW2(boolean isW2) {
this.isW2 = isW2;
}
#Column(name = "AutoAssign", nullable = false)
public boolean isAutoAssign() {
return this.autoAssign;
}
public void setAutoAssign(boolean autoAssign) {
this.autoAssign = autoAssign;
}
#Column(name = "Active", nullable = false)
public boolean isActive() {
return this.active;
}
public void setActive(boolean active) {
this.active = active;
}
#Column(name = "IsRecordDeleted", nullable = false)
public boolean isIsRecordDeleted() {
return this.isRecordDeleted;
}
public void setIsRecordDeleted(boolean isRecordDeleted) {
this.isRecordDeleted = isRecordDeleted;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterSoftware> getAdjusterSoftwares() {
return this.adjusterSoftwares;
}
public void setAdjusterSoftwares(Set<AdjusterSoftware> adjusterSoftwares) {
this.adjusterSoftwares = adjusterSoftwares;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjusterByOldAdjusterId")
public Set<BankInvoiceCorrection> getBankInvoiceCorrectionsForOldAdjusterId() {
return this.bankInvoiceCorrectionsForOldAdjusterId;
}
public void setBankInvoiceCorrectionsForOldAdjusterId(
Set<BankInvoiceCorrection> bankInvoiceCorrectionsForOldAdjusterId) {
this.bankInvoiceCorrectionsForOldAdjusterId = bankInvoiceCorrectionsForOldAdjusterId;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<BankAdjuster> getBankAdjusters() {
return this.bankAdjusters;
}
public void setBankAdjusters(Set<BankAdjuster> bankAdjusters) {
this.bankAdjusters = bankAdjusters;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<SymbilityAdjuster> getSymbilityAdjusters() {
return this.symbilityAdjusters;
}
public void setSymbilityAdjusters(Set<SymbilityAdjuster> symbilityAdjusters) {
this.symbilityAdjusters = symbilityAdjusters;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "adjuster", optional = false)
public AdjusterCompensation getAdjusterCompensation() {
return this.adjusterCompensation;
}
public void setAdjusterCompensation(AdjusterCompensation adjusterCompensation) {
this.adjusterCompensation = adjusterCompensation;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "adjuster")
public AdjusterContact getAdjusterContact() {
return this.adjusterContact;
}
public void setAdjusterContact(AdjusterContact adjusterContact) {
this.adjusterContact = adjusterContact;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterClaimType> getAdjusterClaimTypes() {
return this.adjusterClaimTypes;
}
public void setAdjusterClaimTypes(Set<AdjusterClaimType> adjusterClaimTypes) {
this.adjusterClaimTypes = adjusterClaimTypes;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterClientExclude> getAdjusterClientExcludes() {
return this.adjusterClientExcludes;
}
public void setAdjusterClientExcludes(Set<AdjusterClientExclude> adjusterClientExcludes) {
this.adjusterClientExcludes = adjusterClientExcludes;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterEquipment> getAdjusterEquipments() {
return this.adjusterEquipments;
}
public void setAdjusterEquipments(Set<AdjusterEquipment> adjusterEquipments) {
this.adjusterEquipments = adjusterEquipments;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<ClaimAdjuster> getClaimAdjusters() {
return this.claimAdjusters;
}
public void setClaimAdjusters(Set<ClaimAdjuster> claimAdjusters) {
this.claimAdjusters = claimAdjusters;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<ClaimReport> getClaimReports() {
return this.claimReports;
}
public void setClaimReports(Set<ClaimReport> claimReports) {
this.claimReports = claimReports;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<Invoice> getInvoices() {
return this.invoices;
}
public void setInvoices(Set<Invoice> invoices) {
this.invoices = invoices;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<LicenseAdjuster> getLicenseAdjusters() {
return this.licenseAdjusters;
}
public void setLicenseAdjusters(Set<LicenseAdjuster> licenseAdjusters) {
this.licenseAdjusters = licenseAdjusters;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterClientContactExclude> getAdjusterClientContactExcludes() {
return this.adjusterClientContactExcludes;
}
public void setAdjusterClientContactExcludes(Set<AdjusterClientContactExclude> adjusterClientContactExcludes) {
this.adjusterClientContactExcludes = adjusterClientContactExcludes;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjusterByNewAdjusterId")
public Set<BankInvoiceCorrection> getBankInvoiceCorrectionsForNewAdjusterId() {
return this.bankInvoiceCorrectionsForNewAdjusterId;
}
public void setBankInvoiceCorrectionsForNewAdjusterId(
Set<BankInvoiceCorrection> bankInvoiceCorrectionsForNewAdjusterId) {
this.bankInvoiceCorrectionsForNewAdjusterId = bankInvoiceCorrectionsForNewAdjusterId;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "adjuster", optional = false)
public AdjusterAutoAssign getAdjusterAutoAssign() {
return this.adjusterAutoAssign;
}
public void setAdjusterAutoAssign(AdjusterAutoAssign adjusterAutoAssign) {
this.adjusterAutoAssign = adjusterAutoAssign;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "adjuster", cascade = CascadeType.REMOVE)
public Set<AdjusterLossType> getAdjusterLossTypes() {
return this.adjusterLossTypes;
}
public void setAdjusterLossTypes(Set<AdjusterLossType> adjusterLossTypes) {
this.adjusterLossTypes = adjusterLossTypes;
}
#Override
public Integer getId() {
return getAdjusterId();
}
#Override
public void setId(Integer id) {
setAdjusterId(id);
}
}
HelloController.java
#Autowired
#RequestMapping("/testGeneric")
public #ResponseBody String indexTest6(GenericRepository2<Adjuster, Integer> genericDao) throws JsonProcessingException {
Adjuster adjuster = (Adjuster) genericDao.findById(5567);
ObjectMapper mapper = new ObjectMapper();
Hibernate4Module module = new Hibernate4Module();
mapper.registerModule(module);
String result = mapper.writeValueAsString(adjuster);
return result;
}

That's not how it works.
You have to provide a repository interface with a concrete class for Spring Data to generate the implementation.
Like this:
public interface AdjusterRepository extends PagingAndSortingRepository<Adjuster, Long> {
#Query("SELECT g " // Don't know what this is doing
+ "FROM " + "#{#entityName}" + " g "
+ "WHERE g.#{#entityName}Id = ?1"
)
public Adjuster findById(long id);
}
In your case, I see you try to reuse your custom query for different classes. But Spring will pick up your GenericRepository and won't know what to do.
The correct approach is to mark this GenericRepositoy as #NoRepositoryBean so Spring doesn't try to instantiate it at runtime, which is the cause of your error.
So you can do this:
#NoRepositoryBean
public interface GenericRepository<T> extends PagingAndSortingRepository<T, Long> {
#Query("SELECT g " // Don't know what this is doing
+ "FROM " + "#{#entityName}" + " g "
+ "WHERE g.#{#entityName}Id = ?1"
)
public T findById(T id);
}
public interface AdjusterRepository extends GenericRepository<Adjuster> {
// will inherit from GenericRepository
}
By doing this, Spring will ignore the GenericRepository, avoiding you error, and create a concrete implementation only for AdjusterRepository, and any others that you subclass from GenericRepository.
I didn't test this code, there may be some typos. I recommend reading this section from the Spring Data reference: Defining repository interfaces

Related

Problems persisting an entity (jpa), which is a joined-table, with some extra fields, in the context of a Spring-Batch

The Batch, that reads two tables, modifies them, and writes them back to an external DB (socio_db), is part of a Micro-Services Demo Project. The DB-update of the Spring-Batch runs on a schedule (each 24 hours), modifies two fields only (boolean and Enum).
(The Table to be modified, belongs to a Facebook-like application, where socios register and add other socios as an associated-socio):
socio_db schema
Before showing the code and the error message:
I have configured the Batch Meta-Data DB (socio_batch_meta_data_db) with its own DataSource, so it stays apart of socio_db which is the target DB;
I will not include the logic concerning the two DB-fields since it is not relevant for the reader-prossesor-writer flow problem of the SocioAssociatedSocio class;
Find only the code of the three relevant entities (Socio, SocioAssociatedSocio, SocioAssociatedSocioId).
Error Message:
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 and Property Files:
<?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
DB and JobRepositoy config:
#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();
}
}
The Three Entities:
#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", nullable = false, 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", joinColumns = #JoinColumn(name = "socio_id"),
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") //, catalog = "socio_db") , 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", updatable = false, insertable = false,
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;
}
}
The Batch-Flow
#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();
}
}
Reader:
#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;
}
}
}
Processor:
#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;
}
}
Writer:
#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);
}
}
Final Comment:
The Socio reader-processor-writer, which works, is identical to the one shown above. The problem lays in the fact that the SocioAssociatedSocio, which is a Joined table (with extra fields), is different from the Socio-entity (which is a regular entity).
I believe there is some issue with transaction management and entitymanager (factories).
Any help, comments improvements are welcome.
thanks for looking into this
willem

Postman request doesn't work - Rest APi implementation

I have a problem to create a POST or a GET request in postman. I have an controller that suppose to save a new project or to receive all projects from DB.
If I try to save a project in a class that implements CommandLineRunner in database it's everything ok. For more extra details I put my code below:
This is Project class:
#Entity
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "proj_id")
private int projectId;
#Column(name = "project_name")
private String projectName;
#Column(name = "dg_number")
private int dgNumber;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "variant_gate_relation",
joinColumns = {#JoinColumn(name = "proj_id")},
inverseJoinColumns = {#JoinColumn(name = "gate_id")}
)
private Set<Gate> gates = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "variant_threshold_relation",
joinColumns = #JoinColumn(name = "proj_id"),
inverseJoinColumns = #JoinColumn(name = "threshold_id"))
private Set<Threshold> thresholds = new HashSet<>();
public Project() {
}
public Project(String projectName, int dgNumber, Set<Gate> gates, Set<Threshold> thresholds){
this.projectName = projectName;
this.dgNumber = dgNumber;
this.gates = gates;
this.thresholds = thresholds;
}
Controller
#RestController
#RequestMapping(ProjectController.PROJECT_URL)
public class ProjectController {
public static final String PROJECT_URL = "/cidashboard/projects";
#Autowired
private final ProjectService projectService;
public ProjectController(ProjectService projectService) {
this.projectService = projectService;
}
#GetMapping
public List<Project> getAllProjects(){
return projectService.findAllProjects();
}
#GetMapping("/{id}")
public Project getProjectById(#PathVariable int id) {
return projectService.findProjectById(id);
}
#PostMapping
// #Consumes(MediaType.APPLICATION_JSON_VALUE)
public Project saveProject(#RequestBody Project newProj) {
return projectService.saveProject(newProj);
}
}
This is my CommandLineRunner
#Component
public class Test implements CommandLineRunner {
private final ProjectRepository projectRepository;
private final GateRepository gateRepository;
private final ThresholdRepository thresholdRepository;
public Test(ProjectRepository projectRepository, GateRepository gateRepository, ThresholdRepository thresholdRepository) {
this.projectRepository = projectRepository;
this.gateRepository = gateRepository;
this.thresholdRepository = thresholdRepository;
}
#Override
public void run(String... args) throws Exception {
Gate gate1 = new Gate("gate5", 23);
gateRepository.save(gate1);
Threshold threshold1 = new Threshold(101, "threshold5");
thresholdRepository.save(threshold1);
Set<Gate> gates = new HashSet<>();
gates.add(gate1);
Set<Threshold> thresholds = new HashSet<>();
thresholds.add(threshold1);
Project project1 = new Project("project1", 20, gates, thresholds);
projectRepository.save(project1);
List<Project> allProjectsFromDatabase = projectRepository.findAll();
System.out.println("List of all projects from database : ");
for (Project project : allProjectsFromDatabase) {
System.out.println(project.toString());
}
System.out.println("---------------------------------------------");
List<Gate> allGatesFromDatabase = gateRepository.findAll();
for (Gate gate : allGatesFromDatabase) {
System.out.println(gate);
}
}
}
My output from console is :
1 project1 201 gate5 23.0 threshold5 101
I try to do this request from Postman:
{
"projectName": "project2",
"dgnumber": 1,
"gates": {
"gateType" : "gate2",
"gateValue" : 13
},
"thresholds": {
"thresholdType" : "threshold2",
"thresholdValue" : 22
}
}
And I receive the following output :
{
"projectId": 3,
"projectName": "project2",
"dgnumber": 1
}
And in DB only in project table the data was save, in gate table, threshold table, variant_gate_relation and variant_threshold_relation didn't save nothing
In your CommandLineRunner, you do this before saving the project.
Gate gate1 = new Gate("gate5", 23);
gateRepository.save(gate1);
Threshold threshold1 = new Threshold(101, "threshold5");
thresholdRepository.save(threshold1);
Set<Gate> gates = new HashSet<>();
gates.add(gate1);
Set<Threshold> thresholds = new HashSet<>();
thresholds.add(threshold1);
You should replicate that when saving from endpoint as well (the gateRepository.save() and thresholdRepository.save() are relevant here).

How to increase performance in EclipseLink when dealing with BLOB fields?

My Java application has become very slow due to the BLOB field that each entity has. This field is usually used to store a PDF file and whenever i have to list all the objects, it takes a considerable amount of time until the persistence provider can finish its job. I looked for answers on how to deal with such type of data, but some of them talk about storing the BLOB in a separate table and then using FetchType.LAZY. Is there any way to fetch this field only when needed without having to create another table? If not, is creating another table the most appropriate solution?
Entity Code
#Cache(alwaysRefresh = true)
public class ScdDocumento implements Serializable, MultipleSelector {
#Transient
public static final String QUERY_RELATORIO_DOC_LOC = "consultas/ctrl_docs/consulta_relatorio_doc_local.txt";
#Transient
public static final String QUERY_RELATORIO_DOC_GRUPO = "consultas/ctrl_docs/consulta_relatorio_doc_grupo.txt";
#Id
#Column(name = "nome", length = 50)
private String nome;
#Column(name = "revisao")
private int revisao;
#Column(name = "id_tipo")
private int id_tipo;
#Column(name = "situacao", length = 1)
private String situacao;
#Column(name = "doc_blob_nome", length = 50)
private String doc_blob_nome;
#Lob
#Basic(fetch = FetchType.LAZY)
#Column(name = "documento_blob", nullable = false)
private byte[] documento_blob; //The field that impacts the application perfomance
#Column(name = "abrangencia_geral")
private int abrangencia_geral;
#ManyToMany
#JoinTable(name = "SCD_DOC_GRUPO", joinColumns = {#JoinColumn(name = "id_doc")},
inverseJoinColumns = {#JoinColumn(name = "id_grupo")})
private Set<SosGrupo> grupos;
#ManyToOne
#JoinColumn(name = "id_tipo", insertable = false, updatable = false)
private ScdTipo tipo;
#ManyToMany
#JoinTable(name = "SCD_REFERENCIA", joinColumns = {#JoinColumn(name = "doc_pai")},
inverseJoinColumns = {#JoinColumn(name = "doc_filho")})
private Set<ScdDocumento> referencias;
#ManyToMany
#JoinTable(name = "SCD_REFERENCIA", joinColumns = {#JoinColumn(name = "doc_filho")},
inverseJoinColumns = {#JoinColumn(name = "doc_pai")})
private Set<ScdDocumento> referenciadoPor;
#ManyToMany
#JoinTable(name = "SCD_PALAVRA_REFERENCIA", joinColumns = {#JoinColumn(name = "documento")},
inverseJoinColumns = {#JoinColumn(name = "palavra")})
private Set<ScdPalavraChave> palavrasChaves;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "documento")
private Set<ScdOrdem> ordens;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "documento")
private Set<ScdArquivoOs> arquivosOs;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "documento")
private Set<ScdArquivoHistorico> arquivosHistorico;
#ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE})
#JoinTable(name = "SCD_LOCAL_DOC", joinColumns = {#JoinColumn(name = "id_doc")},
inverseJoinColumns = {#JoinColumn(name = "id_local")})
private Set<ScdLocal> locais;
#Override
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getRevisao() {
return revisao;
}
public void setRevisao(int revisao) {
this.revisao = revisao;
}
public int getIdTipo() {
return id_tipo;
}
public void setIdTipo(int id_tipo) {
this.id_tipo = id_tipo;
}
public String getSituacao() {
return situacao;
}
public void setSituacao(String situacao) {
this.situacao = situacao;
}
public String getDocBlobNome() {
return doc_blob_nome;
}
public void setDocBlobNome(String doc_blob_nome) {
this.doc_blob_nome = doc_blob_nome;
}
public byte[] getDocumentoBlob() {
return documento_blob;
}
public void setDocumentoBlob(byte[] documento_blob) {
this.documento_blob = documento_blob;
}
public int getAbrangenciaGeral() {
return abrangencia_geral;
}
public void setAbrangenciaGeral(int abrangencia_geral) {
this.abrangencia_geral = abrangencia_geral;
}
public Set<SosGrupo> getGrupos() {
return grupos;
}
public void setGrupos(Set<SosGrupo> grupo) {
this.grupos = grupo;
}
public ScdTipo getTipo() {
return tipo;
}
public void setTipo(ScdTipo tipo) {
this.tipo = tipo;
}
public Set<ScdDocumento> getReferencias() {
return referencias;
}
public void setReferencias(Set<ScdDocumento> referencias) {
this.referencias = referencias;
}
public Set<ScdDocumento> getReferenciadoPor() {
return referenciadoPor;
}
public void setReferenciadoPor(Set<ScdDocumento> referenciadoPor) {
this.referenciadoPor = referenciadoPor;
}
public Set<ScdPalavraChave> getPalavrasChaves() {
return palavrasChaves;
}
public void setPalavrasChaves(Set<ScdPalavraChave> palavrasChaves) {
this.palavrasChaves = palavrasChaves;
}
public Set<ScdOrdem> getOrdens() {
return ordens;
}
public void setOrdens(Set<ScdOrdem> ordens) {
this.ordens = ordens;
}
public Set<ScdArquivoOs> getArquivosOs() {
return arquivosOs;
}
public void setArquivosOs(Set<ScdArquivoOs> arquivosOs) {
this.arquivosOs = arquivosOs;
}
public Set<ScdArquivoHistorico> getArquivosHistorico() {
return arquivosHistorico;
}
public void setArquivosHistorico(Set<ScdArquivoHistorico> arquivosHistorico) {
this.arquivosHistorico = arquivosHistorico;
}
public Set<ScdLocal> getLocais() {
return locais;
}
public void setLocais(Set<ScdLocal> locais) {
this.locais = locais;
}
#Override
public String getIdRef() {
return nome;
}
#Override
public String getDesc() {
return tipo.getNome();
}
}
Methods that are causing the problem
GUI
private void loadDocumentTable(String situacao) {
mapaDocumentos = new TreeMap<>();
modelDocumentos.setRowCount(0);
docdao.getCriteria("situacao", situacao).forEach((e) -> {
mapaDocumentos.put(e.getNome(), e);
});
mapaDocumentos.entrySet().forEach((e) -> {
String desc = e.getValue().getDocBlobNome();
modelDocumentos.addRow(new Object[]{e.getKey(), desc.substring(0, desc.length() - 3), e.getValue().getRevisao()});
});
}
Generic Dao
#Override
public List<T> getCriteria(String column, Object value){
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(clazz);
Root<T> root = cq.from(clazz);
EntityType<T> ent = root.getModel();
cq.where(cb.equal(root.get(ent.getSingularAttribute(column)), value.toString()));
return em.createQuery(cq).getResultList();
}
Table Model
Persistence
Added this to my persistence.xml, but eclipselink still fetched the byte[] field eagerly.
Maven Plugin
<plugin>
<groupId>de.empulse.eclipselink</groupId>
<artifactId>staticweave-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>weave</goal>
</goals>
<configuration>
<persistenceXMLLocation>META-INF/persistence.xml</persistenceXMLLocation>
<logLevel>FINE</logLevel>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.5.2</version>
</dependency>
</dependencies>
</plugin>
Final Edit
The staticweave-maven-plugin actually worked, but I need to build the project everytime I change something in order for the performance to be boosted. This happens because the weaving is static, so it's applied at build time and not when running the project using the IDE.
JPA basics also allow specifying a fetch type of LAZY, which would prevent loading the BLOB until you access it in the entity. OneToOne, ManyToOne and basic mappings require byte code enhancement of your entities for EclipseLink to gain notification when you access a lazy attribute and load it, which is described here as weaving. This will ensure that it isn't loaded by default.
With the use of weaving, you can also use entity graphs to specify what is loaded and when. This can allow loading the blob in a single query with the rest of the entity when it is to be used, and exclude it by default elsewhere. See What is the diffenece between FETCH and LOAD for Entity graph of JPA? for info on load and fetch graphs.

Mapping hibernate entity with Jackson annotation

I'm working with Spring, hibernate and MySql but I have some problem with seralization of query result.
First in my entity I added #JsonManagedReference on Set structure (#OneToMany side) and #JsonBackReference on single object reference (#ManyToOne side) and it works but I wasn't be able to retrieve all needed information (for example #ManyToOne reference).
So i swapping #JsonBackReference on set structure and #JsonManagedReference on single object but I retrieve
No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.model.tablesField.TableUI["data"]->java.util.ArrayList[0]->com.domain.Car["carType"]->com.domain.CarType_$$_jvst744_f["handler"])
I tried also with #JsonIgnore on Set structure but it doesn't work for the same issues.
This is my spring configuration
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
// properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
properties.put("hibernate.enable_lazy_load_no_trans",true);
return properties;
and this is part of one of my several entities:
/**
* Car generated by hbm2java
*/
#Entity
#Table(name = "car", catalog = "ATS")
public class Car implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idCar;
#JsonManagedReference
private CarType carType;
#JsonManagedReference
private Fleet fleet;
private String id;
private int initialKm;
private String carChassis;
private String note;
#JsonBackReference
private Set<Acquisition> acquisitions = new HashSet<Acquisition>(0);
public Car() {
}
public Car(CarType carType, Fleet fleet, int initialKm, String carChassis) {
this.carType = carType;
this.fleet = fleet;
this.initialKm = initialKm;
this.carChassis = carChassis;
}
public Car(CarType carType, Fleet fleet, String id, int initialKm, String carChassis, String note,
Set<Acquisition> acquisitions) {
this.carType = carType;
this.fleet = fleet;
this.id = id;
this.initialKm = initialKm;
this.carChassis = carChassis;
this.note = note;
this.acquisitions = acquisitions;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_car", unique = true, nullable = false)
public Integer getIdCar() {
return this.idCar;
}
public void setIdCar(Integer idCar) {
this.idCar = idCar;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_carType", nullable = false)
public CarType getCarType() {
return this.carType;
}
public void setCarType(CarType carType) {
this.carType = carType;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_fleet", nullable = false)
public Fleet getFleet() {
return this.fleet;
}
public void setFleet(Fleet fleet) {
this.fleet = fleet;
}
#Column(name = "id", length = 5)
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
#Column(name = "initialKm", nullable = false)
public int getInitialKm() {
return this.initialKm;
}
public void setInitialKm(int initialKm) {
this.initialKm = initialKm;
}
#Column(name = "carChassis", nullable = false, length = 20)
public String getCarChassis() {
return this.carChassis;
}
public void setCarChassis(String carChassis) {
this.carChassis = carChassis;
}
#Column(name = "note", length = 100)
public String getNote() {
return this.note;
}
public void setNote(String note) {
this.note = note;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "car")
public Set<Acquisition> getAcquisitions() {
return this.acquisitions;
}
public void setAcquisitions(Set<Acquisition> acquisitions) {
this.acquisitions = acquisitions;
}
}
one method that uses the query:
#Override
#RequestMapping(value = { "/cars/{idFleet}"}, method = RequestMethod.GET)
public #ResponseBody TableUI getCars(#PathVariable int idFleet) {
TableUI ajaxCall=new TableUI();
try {
ajaxCall.setData(fleetAndCarService.findCarsByIdFleet(idFleet));
return ajaxCall;
} catch (QueryException e) {
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
LOG.error("Threw exception in FleetAndCarControllerImpl::addCar :" + errorResponse.getStacktrace());
return ajaxCall;
}
}
two class for the query:
public interface DefRdiRepository extends JpaRepository<DefRdi, Integer>{
//#Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM DefRdi c WHERE c.parName = ?1 AND c.description= ?2")
//Boolean existsByParNameAndDescription(String parName, String description);
//Query method of spring, I put findBy and then the key of research
DefRdi findByParNameAndDescription(String parName, String description);
}
public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {
//Query method of spring, I put findBy and then the key of research
List<Car> findByFleetIdFleet(int idFleet);
}
Where is my error? I don't want Set object but only the single reference. The problem is only when I serialize. Thanks
UPDATE:
I use #JSonIgnore on all set collectionts and Eager instead lazy ad all works fine, but is there a way to retrieve all the information only when I want, for example having two different query?
So it doesn't work
#Override
#Transactional
public List<Car> findByFleetIdFleet(int idFleet) {
List<Car> carList= carRepository.findByFleetIdFleet(idFleet);
for (Car car:carList){
Hibernate.initialize(car.getCarType());
Hibernate.initialize(car.getFleet());
}
return carList;
// return carRepository.findByFleetIdFleet(idFleet);
}
All collections need to be fetched eagerly when loading them from data base, in order to get serialized by Spring. Make sure you fetch them eagerly (e.g. FetchMode.JOIN). You could also swap #JsonManagedReference from wanted fields with #JsonIgnore to black listed fields, Spring automatically serialises every field without annotation.
Update:
Changing the data repository to something like that should work, I am not sure it compiles, but I think you will get the point:
#EntityGraph(value = "some.entity.graph", type = EntityGraph.EntityGraphType.FETCH)
#Query(
value = "SELECT c FROM Car c INNER JOIN FETCH c.acquisitions WHERE c.id = :idFleet"
)
public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {
//Query method of spring, I put findBy and then the key of research
List<Car> findByFleetIdFleet(int idFleet);
}
For more information look at this post and read the official documentation.
Workaround:
There seems to be a workaround, however fetching those collections eager like shown above should have a positive performance impact, since there is no need for loading proxies afterwards. Also no open transactions are needed at controller level.

Spring : detached entity passed to persist

I'm getting this errors when trying to create relation between 2 entities, this time i'm doing this in different way - passing JSON with 2 object into helper class and then getting those object and persisting them, one by one and setting the relation. When i remove setters of relation : 1. newPerson.setKoordynator(koordynatorzyPraktykEntity);
2.koordynatorzyPraktykEntity.setKoordynatorByIdOsoby(newPerson);
then it is persisting both entities without a problem, with setters only first one (KoordynatorzyPraktykEntity) is persisted (idKoordynatora = 1, idOsoby =0, test = test )
Here is the important part of error from POSTMAN ( full log http://pastebin.com/SRmnPMBH )
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
KoordynatorzyEntity:
#Entity
#Table(name = "koordynatorzy_praktyk", schema = "public", catalog = "praktykidb")
public class KoordynatorzyPraktykEntity {
private int idKoordynatoraPraktyk;
private int idOsoby;
private String doTestow;
private OsobyEntity koordynatorByIdOsoby;
private Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku;
#Id
#GeneratedValue
#Column(name = "id_koordynatora_praktyk")
public int getIdKoordynatoraPraktyk() {
return idKoordynatoraPraktyk;
}
public void setIdKoordynatoraPraktyk(int idKoordynatoraPraktyk) {
this.idKoordynatoraPraktyk = idKoordynatoraPraktyk;
}
#Basic
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
}
/*
STUFF
*/
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_osoby", referencedColumnName = "id_osoby", insertable = false , updatable = false)
public OsobyEntity getKoordynatorByIdOsoby() {
return koordynatorByIdOsoby;
}
public void setKoordynatorByIdOsoby(OsobyEntity koordynatorByIdOsoby) {
this.koordynatorByIdOsoby = koordynatorByIdOsoby;
}
#JsonIgnore
#OneToMany(mappedBy = "koordynatorzyByIdKierunku", cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
public Collection<KoordynatorzyKierunkowiEntity> getKoordynatorzyByIdKierunku() {
return koordynatorzyByIdKierunku;
}
public void setKoordynatorzyByIdKierunku(Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku) {
this.koordynatorzyByIdKierunku = koordynatorzyByIdKierunku;
}
OsobyEntity:
#Entity
#Table(name = "osoby", schema = "public", catalog = "praktykidb")
public class OsobyEntity {
private int idOsoby;
private String tytulZawodowy;
private String imie;
private String nazwisko;
private String email;
private String telefonKomorkowy;
private String telefonStacjonarny;
private KoordynatorzyPraktykEntity koordynator;
#Id
#GeneratedValue
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
}
/*
STUFF
*/
#OneToOne(mappedBy = "koordynatorByIdOsoby", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public KoordynatorzyPraktykEntity getKoordynator() {
return koordynator;
}
public void setKoordynator(KoordynatorzyPraktykEntity koordynator) {
this.koordynator = koordynator;
}
KoordynatorzyPraktykService :
public class KoordynatorzyPraktykService implements iKoordynatorzyPraktykService {
#Autowired
private iKoordynatorzyPraktykDAO ikoordynatorzyPraktykDAO;
#Autowired
private iOsobyDAO iosobyDAO;
#Override
public KoordynatorzyPraktykEntity addCoordinator(KoordynatorzyPraktykEntity koordynatorzyPraktykEntity) {
return ikoordynatorzyPraktykDAO.addCoordinator(koordynatorzyPraktykEntity);
}
/*
STUFF
*/
#Override
public OsobyEntity addPerson(OsobyEntity osobyEntity, KoordynatorzyPraktykEntity koordynatorzyPraktykEntity) {
OsobyEntity newPerson = iosobyDAO.addPerson(osobyEntity);
newPerson.setKoordynator(koordynatorzyPraktykEntity);
System.out.println(koordynatorzyPraktykEntity.toString()); //shows idKoordynatora: 1 idOsoby: 0 test: test
System.out.println(newPerson.toString()); //shows idOsoby: 32768 imie: Tomasz nazwisko: Potempa
int idOsoby = newPerson.getIdOsoby();
koordynatorzyPraktykEntity.setIdOsoby(idOsoby);
System.out.println(koordynatorzyPraktykEntity.toString()); //shows idKoordynatora: 1 idOsoby: 32768 test: test
koordynatorzyPraktykEntity.setKoordynatorByIdOsoby(newPerson);
return newPerson;
}
Both DAOs have em.persist(entity)
and POST of KoordynatorzyPraktykController:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<KoordynatorzyPraktykEntity> addCoordinator(#RequestBody Koordynator newCoordinator) {
KoordynatorzyPraktykEntity addCoordinator = ikoordynatorzyPraktykService.addCoordinator(newCoordinator.getKoordynator());
OsobyEntity addPerson = ikoordynatorzyPraktykService.addPerson(newCoordinator.getOsoba(), addCoordinator);
if (addCoordinator !=null && addPerson !=null) {
return new ResponseEntity<KoordynatorzyPraktykEntity>(addCoordinator, HttpStatus.OK);
}
else {
return new ResponseEntity<KoordynatorzyPraktykEntity>(HttpStatus.NOT_FOUND);
}
}
Helper Class Koordynator:
public class Koordynator {
private KoordynatorzyPraktykEntity koordynator;
private OsobyEntity osoba;
public KoordynatorzyPraktykEntity getKoordynator() {
return koordynator;
}
public void setKoordynator(KoordynatorzyPraktykEntity koordynator) {
this.koordynator = koordynator;
}
public OsobyEntity getOsoba() {
return osoba;
}
public void setOsoba(OsobyEntity osoba) {
this.osoba = osoba;
}
}
and this is parsed JSON into controller through POSTMAN
{
"koordynator":
{
"doTestow" : "test"
},
"osoba":
{
"tytulZawodowy" : "inzynier",
"imie" : "Tomasz",
"nazwisko" : "Potempa",
"email" : "tp#tp.pl",
"telefonKomorkowy" : "124675484",
"telefonStacjonarny" : "654786484"
}
}
Only way I got it work
Class A:
#OneToMany(cascade = CascadeType.MERGE)
private List<B> b;
Class B:
#ManyToOne
#JoinColumn(name = "aId", referencedColumnName = "id")
private A a;
private String test;
Service:
A a = new A();
//Create without children
aFacade.create(a);
//items
List<B> list = new ArrayList<>();
B b = new B();
b.setTest("Hello");
b.setA(a);
list.add(b);
//merge
a.setB(list);
aFacade.edit(a);
you hit the exception below simply because the entity isn't in the Entity Manager's session at the moment you are trying to persist it. That's due to laziness of your association.
"detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity;"
Try calling em.merge() to attach it to the session.

Categories

Resources