I am having the following entities which are in ManyToMany relation.
UpdateKeyEntity.java
#Embeddable
public class UpdateKeyEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "product_name")
private String productName;
#Column(name = "product_base_version")
private String productBaseVersion;
#Column(name = "update_level")
private int updateLevel;
#Column(name = "channel")
private String channel;
public UpdateKeyEntity() {
}
public UpdateKeyEntity(String productName, String productBaseVersion, int updateLevel, String channel) {
this.productName = productName;
this.productBaseVersion = productBaseVersion;
this.updateLevel = updateLevel;
this.channel = channel;
}
#Override
public int hashCode() {
return Objects.hash(this.productName, this.productBaseVersion, this.channel, this.updateLevel);
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof UpdateKeyEntity) {
UpdateKeyEntity updateKeyEntity = (UpdateKeyEntity) obj;
return this.productName.equals(updateKeyEntity.getProductName()) && this.productBaseVersion.equals(
updateKeyEntity.getProductBaseVersion()) && this.channel.equals(updateKeyEntity.getChannel()) &&
this.updateLevel == updateKeyEntity.updateLevel;
}
return false;
}
}
UpdateEntity.java
#Entity
#Table(name = Constants.TABLE_NAME)
public class UpdateEntity {
#EmbeddedId
private UpdateKeyEntity updateKeyEntity;
#Column(name = "files_added", columnDefinition = "TEXT")
private String filesAdded;
#Column(name = "files_modified", columnDefinition = "TEXT")
private String filesModified;
#Column(name = "files_removed", columnDefinition = "TEXT")
private String filesRemoved;
#Column(name = "lifecycle_state")
private String lifeCycleState;
#ManyToMany(
cascade = {CascadeType.MERGE, CascadeType.PERSIST}
)
#JoinTable(
name = "update_level_security_advisories",
joinColumns = {
#JoinColumn(name = "product_name", referencedColumnName = "product_name"),
#JoinColumn(name = "product_base_version", referencedColumnName = "product_base_version"),
#JoinColumn(name = "update_level", referencedColumnName = "update_level"),
#JoinColumn(name = "channel", referencedColumnName = "channel")
},
inverseJoinColumns = #JoinColumn(name = "sec_ad_id", referencedColumnName = "id")
)
private Set<SecurityAdvisoryEntity> securityAdvisoryEntityList = new HashSet<>();
public UpdateEntity() {
}
public UpdateEntity(UpdateLevelDescription updateLevelDescription) {
this.updateKeyEntity = new UpdateKeyEntity(updateLevelDescription.getProductName(),
updateLevelDescription.getProductBaseVersion(), updateLevelDescription.getUpdateLevel(),
updateLevelDescription.getChannel());
this.filesAdded = updateLevelDescription.getFilesAdded();
this.filesModified = updateLevelDescription.getFilesModified();
this.filesRemoved = updateLevelDescription.getFilesRemoved();
this.lifeCycleState = updateLevelDescription.getLifeCycleState();
addSecAdd(updateLevelDescription.getSecurityAdvisoryEntitylist);
}
// getter and setters
private void addSecAdd(List<SecurityAdvisoryEntity> securityAdvisoryEntitylist) {
for (SecurityAdvisoryEntity securityAdvisoryEntity : securityAdvisoryEntitylist) {
securityAdvisoryEntityList.add(securityAdvisoryEntity);
securityAdvisoryEntity.getUpdateEntityList().add(this);
}
}
#Override
public int hashCode() {
return Objects.hash(this.updateKeyEntity.getProductBaseVersion(),
this.updateKeyEntity.getChannel(), this.updateKeyEntity.getUpdateLevel(), this.filesAdded,
this.filesModified, this.filesRemoved);
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof UpdateEntity) {
UpdateEntity updateEntity = (UpdateEntity) obj;
return (updateEntity.getUpdateKeyEntity().getProductBaseVersion().equals(this.
getUpdateKeyEntity().getProductBaseVersion()) && updateEntity.getUpdateKeyEntity().getChannel().
equals(this.getUpdateKeyEntity().getChannel()) && updateEntity.getUpdateKeyEntity().getUpdateLevel()
== this.getUpdateKeyEntity().getUpdateLevel());
}
return false;
}
}
SecurityAdvisoryEntity.java
#Entity
#Table(name = "security_advisories")
public class SecurityAdvisoryEntity {
#Id
#Column(name = "id")
private String id;
#ManyToMany(mappedBy = "securityAdvisoryEntityList")
private List<UpdateEntity> updateEntityList = new ArrayList<>();
#Column(name = "overview", columnDefinition = "TEXT")
private String overview;
#Column(name = "severity", columnDefinition = "TEXT")
private String severity;
#Column(name = "description", columnDefinition = "TEXT")
private String description;
#Column(name = "impact", columnDefinition = "TEXT")
private String impact;
#Column(name = "solution", columnDefinition = "TEXT")
private String solution;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "credits", columnDefinition = "TEXT")
private String credits;
public SecurityAdvisoryEntity(SecurityAdvisoryDescription securityAdvisoryDescription) {
this.id = securityAdvisoryDescription.getId();
this.overview = securityAdvisoryDescription.getOverview();
this.severity = securityAdvisoryDescription.getSeverity();
this.description = securityAdvisoryDescription.getDescription();
this.impact = securityAdvisoryDescription.getImpact();
this.solution = securityAdvisoryDescription.getSolution();
this.notes = securityAdvisoryDescription.getNotes();
this.credits = securityAdvisoryDescription.getCredits();
}
// getters and setters
}
When executing the following
public void persistUpdateLevelDescription(UpdateLevelDescription updateLevelDescription) throws UpdatesException {
UpdateEntity updateEntity = new UpdateEntity(updateLevelDescription);
EntityManager entityManager = getEntityManager();
try {
persistEntity(updateEntity, entityManager);
}catch (UpdatesException e) {
//log and throw
}
}
private <T> void persistEntity(T entity, EntityManager entityManager) throws UpdatesException {
int numOfAttempts = 0;
do {
numOfAttempts++;
try {
entityManager.getTransaction().begin();
entityManager.persist(entity);
entityManager.getTransaction().commit();
return;
} catch (PersistenceException persistenceException) {
validatePersistenceException(persistenceException);
}
} while (numOfAttempts <= databaseConnection.getMaxRetries());
throw new UpdatesException("Database connection failed when inserting the provided entity");
}
I am getting the following error,
2020-04-26 21:38:24 WARN SqlExceptionHelper:129 - SQL Error: 1364, SQLState: HY000
2020-04-26 21:38:24 ERROR SqlExceptionHelper:131 - Field 'updateEntity_product_name' doesn't have a default value
2020-04-26 21:38:24 INFO AbstractBatchImpl:193 - HHH000010: On release of batch it still contained JDBC statements
2020-04-26 21:38:24 ERROR ExceptionMapperStandardImpl:39 - HHH000346: Error during managed flush [org.hibernate.exception.GenericJDBCException: could not execute statement]
2020-04-26 21:38:24 ERROR UpdatesException:29 - Database connection failed when inserting 10 update level of wso2i2-5.7.0
org.kasun.updates.updatesms.exception.UpdatesException: Database connection failed when inserting 10 update level of wso2i2-5.7.0
at org.kasun.updates.updatesms.dao.UpdatesRepository.persistUpdateLevelDescription(UpdatesRepository.java:337)
at org.kasun.updates.updatesms.UpdatesManager.persistUpdateLevelDescriptions(UpdatesManager.java:142)
at org.kasun.updates.updatesms.service.UpdatesService.updateInformation(UpdatesService.java:227)
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.wso2.msf4j.internal.router.HttpMethodInfo.invokeResource(HttpMethodInfo.java:187)
at org.wso2.msf4j.internal.router.HttpMethodInfo.invoke(HttpMethodInfo.java:143)
at org.wso2.msf4j.internal.MSF4JHttpConnectorListener.dispatchMethod(MSF4JHttpConnectorListener.java:218)
at org.wso2.msf4j.internal.MSF4JHttpConnectorListener.lambda$onMessage$57(MSF4JHttpConnectorListener.java:129)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.kasun.updates.updatesms.exception.UpdatesException: Exception occurred while connecting to the database
at org.kasun.updates.updatesms.dao.UpdatesRepository.validatePersistenceException(UpdatesRepository.java:740)
at org.kasun.updates.updatesms.dao.UpdatesRepository.persistEntity(UpdatesRepository.java:451)
at org.kasun.updates.updatesms.dao.UpdatesRepository.persistUpdateLevelDescription(UpdatesRepository.java:332)
... 13 more
Caused by: javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:75)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71)
at org.kasun.updates.updatesms.dao.UpdatesRepository.persistEntity(UpdatesRepository.java:448)
... 14 more
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:56)
... 16 more
Caused by: org.hibernate.exception.GenericJDBCException: could not execute statement
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2934)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3434)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1397)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:473)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3133)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2370)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:220)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
... 15 more
Caused by: java.sql.SQLException: Field 'updateEntity_product_name' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3847)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3783)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2447)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2594)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1901)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2113)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2049)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2034)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:147)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
... 32 more
What I am doing wrong?
Related
My configuration is the same as the Quarkus guide. I can now query from my database, but trying to insert produces this exception. I am VERY experienced in JPA using Eclipselink, so I know my Entity class is not the problem, as I can query the database with it using a standard JPQL syntax.
The insert fails on a simple em.persist(entity).
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not insert
Caused by: org.postgresql.util.PSQLException: Unsupported Types value: 1,426,407,511
My pom.xml:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
My Entity code:
package com.lmco.is3.nc.micro.datasvc.jpa.entity;
import com.lmco.is3.data.uci.NotificationSeverityType;
import com.lmco.is3.data.uci.NotificationStateType;
import com.lmco.is3.nc.micro.datasvc.jpa.converter.PostgresUuidConverter;
import java.io.Serializable;
import java.util.Objects;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "ALERT_NOTIFICATION")
#SuppressWarnings("unused")
public class AlertNotificationEntity implements Serializable {
#Id
#Convert(converter = PostgresUuidConverter.class)
#Column(name = "ALERT_NOTIFICATION_UID", nullable = false, updatable = false)
private UUID alertNotificationUid;
public UUID getAlertNotificationUid() {
return alertNotificationUid;
}
public void setAlertNotificationUid(UUID alertNotificationUid) {
this.alertNotificationUid = alertNotificationUid;
}
#Convert(converter = PostgresUuidConverter.class)
#Column(name = "SUBJECT_UID")
private UUID subjectUid;
public UUID getSubjectUid() {
return subjectUid;
}
public void setSubjectUid(UUID subjectUid) {
this.subjectUid = subjectUid;
}
/*
#ElementCollection
#CollectionTable(name = "ALERT_NOTIF_ENTITY_PERSPECTIVE",
joinColumns = #JoinColumn(name = "ALERT_NOTIFICATION_UID", referencedColumnName = "ALERT_NOTIFICATION_UID"))
#Enumerated(EnumType.ORDINAL)
#OrderColumn
#Column(name = "ENTITY_PERSPECTIVE_TYPE")
private List<EntityPerspectiveType> entityPerspectiveTypes;
public List<EntityPerspectiveType> getEntityPerspectiveTypes() {
return entityPerspectiveTypes;
}
public void setEntityPerspectiveTypes(List<EntityPerspectiveType> entityPerspectiveTypes) {
this.entityPerspectiveTypes = entityPerspectiveTypes;
}
*/
#Enumerated(EnumType.ORDINAL)
#Column(name = "NOTIFICATION_STATE")
private NotificationStateType state;
public NotificationStateType getState() {
return state;
}
public void setState(NotificationStateType state) {
this.state = state;
}
#Enumerated(EnumType.ORDINAL)
#Column(name = "NOTIFICATION_SEVERITY")
private NotificationSeverityType severity;
public NotificationSeverityType getSeverity() {
return severity;
}
public void setSeverity(NotificationSeverityType severity) {
this.severity = severity;
}
#Column(name = "NOTIFICATION_TIME")
private double notificationTime;
public double getNotificationTime() {
return notificationTime;
}
public void setNotificationTime(double notificationTime) {
this.notificationTime = notificationTime;
}
#Convert(converter = PostgresUuidConverter.class)
#Column(name = "SYSTEM_UID")
private UUID systemUid;
public UUID getSystemUid() {
return systemUid;
}
public void setSystemUid(UUID systemUid) {
this.systemUid = systemUid;
}
/*
#ElementCollection
#CollectionTable(name = "ALERT_NOTIF_APPLIES_TO_ENTITY",
joinColumns = #JoinColumn(name = "ALERT_NOTIFICATION_UID", referencedColumnName = "ALERT_NOTIFICATION_UID"))
#Convert(converter = PostgresUuidConverter.class)
#OrderColumn
#Column(name = "APPLIES_TO_ENTITY_UID")
private List<UUID> appliesToEntityUids;
public List<UUID> getAppliesToEntityUids() {
return appliesToEntityUids;
}
public void setAppliesToEntityUids(List<UUID> appliesToEntityUids) {
this.appliesToEntityUids = appliesToEntityUids;
}
*/
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AlertNotificationEntity that = (AlertNotificationEntity) o;
return Objects.equals(alertNotificationUid, that.alertNotificationUid);
}
#Override
public int hashCode() {
return Objects.hash(alertNotificationUid);
}
}
Well, I resolved my own problem, simpler than I expected.
I changed my usage of PostresUuidConverter to #Type(type = "pg-uuid") and all works now.
I have a ticket object which should be edited. In my ticket object are attributes which reference to object(see below).
...
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "project_id", referencedColumnName="id")
private Project project;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "costCenter_id", referencedColumnName="id")
private CostCenter costCenter;
...
But when I try to update the entity I always get an error:
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
#PutMapping("/tickets/{id}")
#ResponseStatus(HttpStatus.OK)
public Ticket updateTicket(#RequestBody Ticket ticket) throws Exception{
Optional<Ticket> o = this.ticketRepo.findById(ticket.getId());
o.ifPresent(element -> {
if(ticket.getCostCenter() != null) {
Optional<CostCenter> c = this.costCenterRepo.findById(ticket.getCostCenter().getId());
c.ifPresent( costCenter -> {
element.setCostCenter(costCenter);
});
}
if(ticket.getProject() != null) {
Optional<Project> p = this.projectRepo.findById(ticket.getProject().getId());
p.ifPresent(project -> {
element.setProject(project);
});
}
this.ticketRepo.save(element);
});
return o.orElseThrow(() -> new NotFoundException(ticket.getId()));
}
PS: When I trigger the update without changes everything works fine.
Stacktrace: https://textsaver.flap.tv/lists/2vm5
class AuditorAwareImpl implements AuditorAware<Long> {
#Override
public Optional<Long> getCurrentAuditor() {
PersonRepository personRepo = ApplicationContextProvider.getApplicationContext().getBean(PersonRepository.class);
if(SecurityContextHolder.getContext().getAuthentication() != null) {
Person p = personRepo.findByUserPrincipalName(SecurityContextHolder.getContext().getAuthentication().getName() + "#email.com");
return Optional.of(p.getId());
} else {
Person p = personRepo.findByUserPrincipalName("SYSTEM");
return Optional.of(p.getId());
}
}
}
#Component(value = "applicationContextProvider")
class ApplicationContextProvider implements ApplicationContextAware {
private static class AplicationContextHolder {
private static final InnerContextResource CONTEXT_PROV = new InnerContextResource();
}
private static final class InnerContextResource {
private ApplicationContext context;
private void setContext(ApplicationContext context) {
this.context = context;
}
}
public static ApplicationContext getApplicationContext() {
return AplicationContextHolder.CONTEXT_PROV.context;
}
#Override
public void setApplicationContext(ApplicationContext ac) {
AplicationContextHolder.CONTEXT_PROV.setContext(ac);
}
}
#Data
#Getter
#Setter
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
protected Long id;
#CreatedDate
private Date createdAt;
#CreatedBy
private Long createdBy;
#LastModifiedDate
private Date updatedAt;
#LastModifiedBy
private Long updatedBy;
#PrePersist
protected void prePersist() {
if (this.createdAt == null) createdAt = new Date();
if (this.updatedAt == null) updatedAt = new Date();
}
#PreUpdate
protected void preUpdate() {
this.updatedAt = new Date();
}
#PreRemove
protected void preRemove() {
this.updatedAt = new Date();
}
}
You have a StackOverflowError which strongly suggest you've got some infinite recursion somewhere (or at least a very deep one):
Caused by: java.lang.RuntimeException: java.lang.StackOverflowError
The fact that com.mycompany.test.config.AuditorAwareImpl.getCurrentAuditor shows up repeatedly in your very long stack trace suggests that it is somehow involved in your infinite recursion and I'd wager that it comes from this class somehow triggering whatever triggered it in the first place possibly org.springframework.data.auditing.AuditingHandler. So check your AuditorAwareImpl code and/or the auditing configuration.
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.
I am trying to do a query on three tables by using QueryBuilder from OrmLite.
My problem is that my final query returns no results, while doing the query only for Info, InfoFenomeno or Instruccion do return results.
public List<PasoInstruccion> consultar(Request request) {
InfoRequest infoRequest = (InfoRequest) request;
ArrayList<PasoInstruccion> pasos = new ArrayList<>();
List<PasoInstruccion> pasosResult = null;
try {
QueryBuilder<Info, Long> infoQuery = LocalDBHelper.getInstance(context).getInfoDao().queryBuilder();
QueryBuilder<InfoFenomeno, Long> fenomenoQuery = LocalDBHelper.getInstance(context).getInfoFenomenoDao().queryBuilder();
fenomenoQuery.where()
.eq(InfoFenomeno.COLUMN_FENOMENO, infoRequest.getFenomeno());
infoQuery.join(fenomenoQuery);
QueryBuilder<Instruccion, Long> instruccionQuery = LocalDBHelper.getInstance(context).getInstruccionDao().queryBuilder();
instruccionQuery.where()
.eq(Instrucciones.COLUMN_NIVEL_AFECTACION, infoRequest.getNivelAfectacion())
.and()
.eq(Instrucciones.COLUMN_CATEGORIA_INFO, infoRequest.getCategoria())
.and()
.eq(Instrucciones.COLUMN_AMBIENTE_INFO, infoRequest.getSituacion());
QueryBuilder<PasoInstruccion, Long> pasoInstruccionQuery = LocalDBHelper.getInstance(context).getPasoInstruccionDao().queryBuilder();
instruccionQuery.join(infoQuery);
pasoInstruccionQuery.join(instruccionQuery);
pasosResult = pasoInstruccionQuery.query();
} catch (SQLException e) {
e.printStackTrace();
}
return pasosResult;
}
The structure on the DB is like this (simplified):
public class Info {
#DatabaseField(id = true, columnName = Infos.COLUMN_ID)
private long id;
#DatabaseField(columnName = Infos.COLUMN_NOMBRE)
private String nombre;
#ForeignCollectionField(eager = true, columnName = Infos.COLUMN_FENOMENO)
private ForeignCollection<InfoFenomeno> infoFenomenos;
#ForeignCollectionField(eager = true, columnName = Infos.COLUMN_INSTRUCCION)
private ForeignCollection<Instruccion> pInstrucciones;
}
public class InfoFenomeno {
#DatabaseField(generatedId = true, columnName = InfoFenomeno.COLUMN_ID)
private long id;
#DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = InfoFenomeno.COLUMN_INFO)
private Info info;
#DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = InfoFenomeno.COLUMN_FENOMENO)
private Fenomeno fenomeno;
}
public class Instruccion {
#DatabaseField(generatedId = true)
private long id;
#DatabaseField(columnName = Instrucciones.COLUMN_NIVEL_AFECTACION)
private String nivelAfectacionString;
#DatabaseField(columnName = Instrucciones.COLUMN_CATEGORIA_INFO)
private String categoriaInformacionString;
#DatabaseField(columnName = Instrucciones.COLUMN_AMBIENTE_INFO)
private String ambienteInformacionString;
#ForeignCollectionField(eager = true)
private ForeignCollection<PasoInstruccion> pPasosInstruccion;
#DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = Instrucciones.COLUMN_INFO)
private Info info;
}
public class PasoInstruccion {
#DatabaseField(id = true, columnName = PasoInstrucciones.COLUMN_ID)
private long secuencia;
#DatabaseField(columnName = PasoInstrucciones.COLUMN_NOMBRE)
private String nombre;
#DatabaseField(columnName = PasoInstrucciones.COLUMN_INSTRUCCION, foreignAutoRefresh = true, foreign = true)
private Instruccion instruccion;
}
Values in InfoRequest parameter are right.
May be that I am not using QueryBuilder the right way?
EDIT: SQL Statement
SELECT `pasoInstrucciones`.* FROM `pasoInstrucciones` INNER JOIN `instrucciones` ON `pasoInstrucciones`.`_instruccion` = `instrucciones`.`id` INNER JOIN `infos` ON `instrucciones`.`_info` = `infos`.`_id` INNER JOIN `infoFenomeno` ON `infos`.`_id` = `infoFenomeno`.`_info` WHERE ((`instrucciones`.`_nivelAfectacion` = 'BAJO' AND `instrucciones`.`_categoriaInfo` = 'ANTES' ) AND `instrucciones`.`_ambienteInfo` = 'HOGAR' ) AND (`infoFenomeno`.`_fenomeno` = 6 )
Thanks in advance.
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.