I have an entity that has a collection of entities, as bellow:
@Entity
@Table
public class Company extends AbstractEntity implements Serializable {
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.REFRESH,
CascadeType.REMOVE}, targetEntity = Meter.class,
fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "owner")
private Collection<Meter> meters;
public Collection<Meter> getMeters() {
return this.meters;
}
public void addMeter(Meter meter) {
this.meters.add(meter);
}
}
The problem is when I try to associate another "meter" to the "company". I'm trying with this:
private void buttonSaveAsActionPerformed(java.awt.event.ActionEvent evt) {
final Meter edited = meterEditionPanel.getMeter();
if (edited == null)
return;
final Company company = edited.getOwner();
company.addMeter(edited);
try {
service.updateEntity(company);
model.update();
listMeters.setSelectedValue(edited.getAlias(), true);
meterEditionPanel.setMeter(edited);
} catch (Exception ex) {
FieldMarker.showEntityAlreadyExistsInCompanyDialog(
dictionary.getString("commons.title.meter"),
dictionary.getString("commons.text.serialNumber"), false);
LOGGER.debug("Não foi possível atualizar o medidor: " + ex.toString());
}
}
As one can see, I'm using the company update to create the meter. When I run this code, I get an org.hibernate.LazyInitializationException
.
I have tried also to create the "meter" directly, like bellow:
private void buttonSaveAsActionPerformed(java.awt.event.ActionEvent evt) {
final StringListModel<Meter> model = getListModel();
final Meter edited = meterEditionPanel.getMeter();
if (edited == null)
return;
// final Company company = edited.getOwner();
// company.addMeter(edited);
try {
service.createEntity(edited);
model.update();
listMeters.setSelectedValue(edited.getAlias(), true);
meterEditionPanel.setMeter(edited);
} catch (Exception ex) {
FieldMarker.showEntityAlreadyExistsInCompanyDialog(
dictionary.getString("commons.title.meter"),
dictionary.getString("commons.text.serialNumber"), false);
LOGGER.debug("Não foi possível atualizar o medidor: " + ex.toString());
}
}
With this change I got another error:
2016-03-03 11:36:37,145 [AWT-EventQueue-0] ERROR - SqlExceptionHelper:logExceptions:146 - ERROR: duplicate key value violates unique constraint "meter_pkey"
Detalhe: Key (id)=(11) already exists.
But the meter extends AbstractEntity:
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
@Column(unique=true,nullable=false)
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
It seems that the GeneratedValue annotation can't get a correct id to lazily initialized entities.
The createEntity is the method bellow:
public <T extends AbstractEntity> T createEntity(T entity) {
try {
JpaRepository<T, Long> repository = getRepository(entity.getClass());
return repository.saveAndFlush(entity);
} catch (Exception e) {
logger.error("Erro de criação de entidade: " + e.toString());
return null;
}
}
This is the meter class:
package com.metrum.mtuapp.persistence.model;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.springframework.util.Assert;
@Entity
public class Meter extends AbstractEntity implements Serializable {
@Basic
@Column(nullable = false)
private String serialNumber;
@Basic
@Column(nullable = false, unique = true)
private String identification;
@ManyToOne(optional = false, targetEntity = Company.class)
private Company owner;
@Basic
private String description;
@ManyToOne(optional = false, targetEntity = MeterModel.class)
private MeterModel meterModel;
@OneToMany(cascade = {CascadeType.ALL}, targetEntity = Calibration.class,
orphanRemoval = true, mappedBy = "meter")
private Collection<Calibration> calibrations;
public Meter() {
}
public Meter(String description, String serialNumber,
Company owner, MeterModel meterModel) {
Assert.hasText(serialNumber);
Assert.notNull(meterModel);
Assert.notNull(owner);
this.description = description;
this.serialNumber = serialNumber;
this.owner = owner;
this.meterModel = meterModel;
this.identification = getAlias(owner.getName(), serialNumber);
}
private static String getAlias(String companyName, String serialNumber) {
return serialNumber + " [" + companyName + "]";
}
public Collection<Calibration> getCalibrations() {
return this.calibrations;
}
public void addCalibration(Calibration calibration) {
this.calibrations.add(calibration);
}
public String getSerialNumber() {
return this.serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
this.identification = getAlias(owner.getName(), serialNumber);
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public MeterModel getMeterModel() {
return this.meterModel;
}
public void setMeterModel(MeterModel meterModel) {
this.meterModel = meterModel;
}
public Company getOwner() {
return this.owner;
}
public void setOwner(Company owner) {
this.owner = owner;
this.identification = getAlias(owner.getName(), serialNumber);
}
public String getIdentification() {
return identification;
}
public void setIdentification(String identification) {
this.identification = identification;
}
public void copy(Meter edited) {
this.identification = edited.identification;
this.description = edited.description;
this.meterModel = edited.meterModel;
this.owner = edited.owner;
this.serialNumber = edited.serialNumber;
}
@Override
public String getAlias() {
return getIdentification();
}
}
And this is the company class:
package com.metrum.mtuapp.persistence.model;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table
public class Company extends AbstractEntity implements Serializable {
@Column(unique = true, nullable = false)
@Basic
private String name;
@Basic
private String address;
@Basic
private String telephone;
@Basic
private String email;
@Basic
private String note;
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.REFRESH,
CascadeType.REMOVE}, targetEntity = Employee.class,
fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "company")
private Collection<Employee> employees;
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.REFRESH,
CascadeType.REMOVE}, targetEntity = Standard.class,
fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "owner")
private Collection<Standard> standards;
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.REFRESH,
CascadeType.REMOVE}, targetEntity = Meter.class,
fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "owner")
private Collection<Meter> meters;
public Company() {
}
public Company(String name, String address, String telephone, String email,
String note) {
this.name = name;
this.address = address;
this.telephone = telephone;
this.email = email;
this.note = note;
}
public Collection<Standard> getStandards() {
return this.standards;
}
public void addStandard(Standard standard) {
this.standards.add(standard);
}
public String getNote() {
return this.note;
}
public void setNote(String note) {
this.note = note;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getTelephone() {
return this.telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public Collection<Employee> getEmployees() {
return this.employees;
}
public void addEmployee(Employee employee) {
this.employees.add(employee);
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public Collection<Meter> getMeters() {
return this.meters;
}
public void addMeter(Meter meter) {
this.meters.add(meter);
}
public Meter getMeter(String serialNumber) {
for (Meter meter : meters) {
if (meter.getSerialNumber().equals(serialNumber)) {
return meter;
}
}
return null;
}
public Standard getStandard(String serialNumber) {
for (Standard standard : standards) {
if (standard.getSerialNumber().equals(serialNumber)) {
return standard;
}
}
return null;
}
public void copy(Company source) {
setName(source.getName());
setAddress(source.getAddress());
setEmail(source.getEmail());
setTelephone(source.getTelephone());
setNote(source.getNote());
}
@Override
public String getAlias() {
return getName();
}
}
What is the best way to solve this problem, without using Earge initialization.
Possible sulution
SessionFactory sessionFactory = context.getBean(SessionFactory.class);
final Session session = sessionFactory.openSession();
session.beginTransaction();
session.persist(edited);
session.getTransaction().commit();
session.close();
It works fine, but I'll wait for the more suitable solution for this problem.
Thanks!