org.hibernate.TransientObjectException: The given object has a null identifier - spring

I got the below Exception when update my Modelclass
18:27:15,203 ERROR [com.sinergia.ea.daoimpl.TypeOfArtifactDaoImpl] ERROR Exception in updateTypeOfArtifact() : o
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.getUpdateId(DefaultSaveOrUpdateEventListener.
at org.hibernate.event.def.DefaultUpdateEventListener.getUpdateId(DefaultUpdateEventListener.java:46) [:3
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventList
at org.hibernate.event.def.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListen
at org.hibernate.impl.SessionImpl.fireUpdate(SessionImpl.java:564) [:3.2.6.ga]
at org.hibernate.impl.SessionImpl.update(SessionImpl.java:552) [:3.2.6.ga]
at org.hibernate.impl.SessionImpl.update(SessionImpl.java:544) [:3.2.6.ga]
at com.sinergia.ea.daoimpl.TypeOfArtifactDaoImpl.updateTypeOfArtifact(TypeOfArtifactDaoImpl.java:67) [:]
Model Class :
#Entity
#Table(name="TYPE_OF_ARTIFACT")
public class TypeOfArtifactModel implements java.io.Serializable , Identifiable{
/**
*
*/
private static final long serialVersionUID = 2662289176706818360L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TYPE_OF_ARTIFACT_SEQ")
#SequenceGenerator(name = "TYPE_OF_ARTIFACT_SEQ", sequenceName = "TYPE_OF_ARTIFACT_SEQ")
#Column(name="ID",unique=true, nullable=false)
private Integer id;
#Column(name="DESCRIPTION", nullable=true, length=400)
private String description;
#Column(name="NAME", nullable=false, length=50)
private String name;
#OneToMany(fetch = FetchType.LAZY, targetEntity = AdditionalInfoModel.class, mappedBy = "typeOfArtifactID")
private Set<AdditionalInfoModel> additionalInfos = new HashSet<AdditionalInfoModel>(0);
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "TYPE_ARTIFACT_OPERATE_RELATION", joinColumns = { #JoinColumn(name = "TYPE_OF_ARTIFACT_ID") }, inverseJoinColumns = { #JoinColumn(name = "OPERATE_ARTIFACT_ID") })
private Set<TypeOfArtifactModel> checkedItems = new HashSet<TypeOfArtifactModel>(0);
#Column(name="FLAG",length=1)
boolean editable;
public TypeOfArtifactModel() {
}
DaoImppl implementation :
#Override
#Transactional(readOnly = true)
public Boolean updateTypeOfArtifact(#NotNull final TypeOfArtifactModel tipoModel,final Set<AdditionalInfoModel> additionalInfos,final Set<TypeOfArtifactModel> checkedItems) {
try {
System.out.println("Dao Impl Name :"+tipoModel.getName());
System.out.println("Dao Impl Description :"+tipoModel.getDescription());
System.out.println("Dao Impl CheckedItems :"+tipoModel.getCheckedItems());
if(additionalInfos !=null && !(additionalInfos.isEmpty())){
for(AdditionalInfoModel item : additionalInfos){
getSession().update(item);
}
tipoModel.setAdditionalInfos(additionalInfos);
}
getSession().update(tipoModel);
return Boolean.TRUE;
} catch (Exception e) {
log.error(" ERROR Exception in updateTypeOfArtifact() ", e);
return Boolean.FALSE;
}
}
I got the above exception only when i use the update() method if i use the saveOrUpdate() there is no exception but in saveOrUpdate() method new record has created, its not update the record, Could you please tell me whats the wrong in that

The method in which you're trying to update your entity is annotated as #Transactional(readOnly = true). Is that deliberate? That seems wrong.
The problem is that you've passed an object to Hibernate that doesn't have a row in the database with a matching id.
In DefaultSaveOrUpdateEventListener.getUpdateId Hibernate attempts to the read the identifier from the object you're updating but finds that it's null.
Are you sure that the object you're trying to update was previously saved? Is the #Id null at the point that it's loaded? What is the value of the ID column for this entity in the database? Has anything

Related

Spring + Hibernate without lazy = LazyInitializationException

I want to load all objects from a table without a lazy objects/children and list them on the page (Thymeleaf template), but I get a LazyInitializationException every time. I tried to convert Hibernate entity objects into a POJO that doesnt contains a lazy/unwanted object but with the same result. I also tried open-in-view parameter set to false...
Simple example:
Parent:
#Entity
public class DocumentDbe implements Serializable {
public DocumentDbe(){
}
#Id
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private DocumentFileDbe documentFile;
....
}
Child:
#Entity
public class DocumentFileDbe implements Serializable {
public DocumentFileDbe(){}
#Id
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#Column
#Lob
private byte[] documentData;
...
}
POJO:
public class DocumentDto implements Serializable {
public DocumentDto(){
}
public DocumentDto(DocumentDbe doc){
this.id = doc.getId();
}
....
}
Controller:
#GetMapping("/list")
String getList(Model model) {
List<DocumentDbe> docs;
List<DocumentDto> data = new ArrayList<>();
try (Session ses = sessionFactory.openSession()) {
docs = ses.createQuery("FROM DocumentDbe").list();
docs.forEach(doc -> {
data.add(new DocumentDto(doc));
});
}
model.addAttribute(MODEL_LIST_DATA, data);
return "list";
}
EDIT: Thrown exception:
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/list.html]")] with root cause
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
EDIT2:
In DocumentDbe is relation with another object (EAGER this time so I was not paying attention to it) , which has reference to DocumentDbe again.. chained relationship and LazyInitializationException is created...
EDIT3:
Although
This is modified and working controller, without POJO:
#GetMapping("/list")
String getList(Model model) {
List<DocumentDbe> docs;
try (Session ses = sessionFactory.openSession()) {
docs = ses.createQuery("FROM DocumentDbe ORDER BY id DESC").list();
docs.forEach(doc -> {
doc.setDocumentFile(null);
doc.getHistory().forEach(log ->{
log.setDocument(null);
});
});
}
model.addAttribute(MODEL_ADMIN_DATA, docs);
return "list";
}
In class DocumentDbe you have mark relation as Lazy. In default relation #ManyToOne and #OneToOne is as EAGER, so if you don't want Lazy, you have to change
#OneToOne(cascade = CascadeType.PERSIST)
If you want have #lob also as eager:
#Lob
#Basic( fetch = FetchType.EAGER )

Hibernate - Spring - ConstraintViolationException - UniqueConstraint

I'm trying to make some fixtures for my Profile model but every time I'm trying to save it "again" after I did an update, I get this message:
nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
This is my Profile class:
#Entity
#Data
#Builder
#ToString(of = {"birthday", "discordId", "description", "spokenLanguages"})
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "profile", uniqueConstraints = #UniqueConstraint(columnNames = "discordId"))
public class Profile implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idProfile;
private Date birthday;
#Column(name="discordId", insertable=true, updatable=false)
private String discordId;
private String description;
#ElementCollection(fetch = FetchType.EAGER)
private Set<String> spokenLanguages = new LinkedHashSet<String>();
#JsonIgnore
#OneToMany(fetch = FetchType.EAGER)
private Set<ProfileGame> profileGames = new LinkedHashSet<>();
#OneToOne(mappedBy = "profile", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
private User user;
#ManyToOne
private TimeSlot timeSlot;
}
Here is the call:
#Order(7)
#Test
void fillProfileGame() {
List<Profile> profileList = this.profileRepository.findAll();
for (Profile profile : profileList) {
List<Game> gameList = this.gameRepository.findAll();
Collections.shuffle(gameList);
int rndNbGame = new Random().ints(1, 5).findFirst().getAsInt();
for (int i = 1; i <= rndNbGame; i++) {
int rndLevel = new Random().ints(1, 100).findFirst().getAsInt();
int rndRanking = new Random().ints(1, 3000).findFirst().getAsInt();
Game rndGame = gameList.get(0);
gameList.remove(0);
ProfileGame profileGames = new ProfileGame(profile, rndGame, "level-" + rndLevel,
"ranking-" + rndRanking);
this.profileGameRepository.save(profileGames);
this.gameRepository.save(rndGame);
}
this.profileRepository.save(profile);
}
}
So what I understand is that Hibernate won't let me update this object because it has a unique contraint field ?
How do we proceed when we want a field to be unique and still being able to update other fields ?
From the code snippet, what I see is that there are some unique constraints applied on the column 'discordId'.
#Table(name = "profile", uniqueConstraints = #UniqueConstraint(columnNames = "discordId"))
and
#Column(name="discordId", insertable=true, updatable=false)
private String discordId;
As you can see, there is a parameter 'updatable' which is set to false. Therefore, when you are trying to update an already existing object, hibernate is throwing UniqueConstraintViolationException.
To fix this, set 'updatable=true' or remove it altogether and it should work fine.
#Column(name="discordId", insertable=true, updatable=true)
private String discordId;

Mapping DTO to existing entity , it is creating new entity

I have Activity class and ActivityDTO as you see below. While saving new entity there is no problem, but if I want to update existing entity, then it is creating new entity although my entity id is from my database entity.
#Getter
#Setter
#Entity
public class Activity implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue
#SequenceGenerator
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
#JoinTable(name = "ACTIVITY_ATTACHMENT", joinColumns = {
#JoinColumn(name = "ACTIVITY_ID") }, inverseJoinColumns = { #JoinColumn(name = "ATTACHMENT_ID") })
private Set<Attachment> attachments = new HashSet<>();
#Temporal(TemporalType.DATE)
#JsonSerialize(using = CustomJsonDateSerializer.class)
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
#Column(name = "BEGIN_DATE")
private Date beginDate;
#Temporal(TemporalType.DATE)
#JsonSerialize(using = CustomJsonDateSerializer.class)
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
#Column(name = "END_DATE")
private Date endDate;}
And my ActivityDTO
#Getter
#Setter
public class ActivityDTO {
private Long id;
private Set<Attachment> attachments = new HashSet<>();
#Temporal(TemporalType.DATE)
#JsonSerialize(using = CustomJsonDateSerializer.class)
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
private Date beginDate;
#Temporal(TemporalType.DATE)
#JsonSerialize(using = CustomJsonDateSerializer.class)
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
private Date endDate;
And here is my ActivityController class;
public Activity save(ActivityDTO activityDTO, List<MultipartFile> fileList) throws Exception {
Activity activity = convertActivityDTOtoEntity(activityDTO);
activity.getAttachments().addAll(ObjectFactory.createAttachment(fileList, Activity.class));
return activityRepository.save(activity);
}
public Activity update(ActivityDTO activityDTO, List<MultipartFile> fileList) throws Exception {
Activity activity = convertActivityDTOtoEntity(activityDTO);
activity.getAttachments().addAll(ObjectFactory.createAttachment(fileList, Activity.class));
return activityRepository.save(activity);
}
private Activity convertActivityDTOtoEntity(ActivityDTO activityDTO) {
return modelMapper.map(activityDTO, Activity.class);
}
Also I have one more problem, I have just transformed my entity usage to DTO objects, until now service was reaching entity directly and while updating if I delete any attachment or add, there was no problem. After I transformed to DTO objects and used like above, there is a problem while updating;
detached entity passed to persist: com.thy.agencycrm.entity.Attachment
And here is my Attachment entity if you would like to see;
#Getter
#Setter
#Entity
public class Attachment implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue
#SequenceGenerator
private Long id;
#Column(name = "MIME_TYPE")
private String mimeType;
Please help me about this problem, I am searhing and trying to solve it for long times.
Thanks you in advance.
I think you just copy the fields into a new object in your converter right?
Default JPA only update the entity if it is in the persistance context and the two object are identical. If you have a detached object, create a new one with in the converter, it will be saved as new record. It does not matter if you set the ID, because the id is generated by the sequence, as you annotated on the entity class.
You can resolve this many ways. The easiest is to load the entity by id, and set the fields from the another object into this managed object.
Updated your Class ActivityController
public Activity save(ActivityDTO activityDTO, List<MultipartFile> fileList) throws Exception {
Activity activity = convertActivityDTOtoEntity(activityDTO);
activity.getAttachments().addAll(ObjectFactory.createAttachment(fileList, Activity.class));
return activityRepository.save(activity);
}
public Activity update(ActivityDTO activityDTO, List<MultipartFile> fileList) throws Exception {
Activity activity = activitiyRepository.findOne(activityDTO.getID());
// This will update the existing activity with activityDTO
modelMapper.map(activityDTO, activity);
activity.getAttachments().addAll(ObjectFactory.createAttachment(fileList, Activity.class));
return activityRepository.save(activity);
}
private Activity convertActivityDTOtoEntity(ActivityDTO activityDTO) {
return modelMapper.map(activityDTO, Activity.class);
}

Return type of JPA Repository 'getOne(id)' Method

I have the following Spring boot service for an object of type Report -
#Service
public class ReportService {
#Autowired
private ReportRepository reportRepository;
#Autowired
private UserRepository userRepository;
/*get all reports */
public List<Report> getAllReports(){
return reportRepository.findAll();
}
/*get a single report */
public Report getReport(Long id){
return reportRepository.getOne(id);
}
//other similar methods....
}
The problem arises while retrieving a single Report. If a report ID is send which doesn't exist, the following error is generated...
DefaultHandlerExceptionResolver : Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could not
write JSON: Unable to find com.interact.restapis.model.Report with id 16;
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Unable to find com.interact.restapis.model.Report with id 16 (through
reference chain:
com.interact.restapis.model.Report_$$_jvst83c_1["fromUserId"])
Below is the code for my Report Controller
#RestController
public class ReportController {
#Autowired
private ReportService reportService;
//Get all reports
#GetMapping("/interactions")
public List<Report> getAllReports() {
return reportService.getAllReports();
}
//Get single report
#GetMapping("/interactions/{id}")
public ResponseEntity<Report> getReport(#PathVariable Long id) {
if(reportService.getReport(id) == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(reportService.getReport(id), HttpStatus.OK);
}
#PostMapping("/interactions")
public ResponseEntity<Report> addReport(#RequestBody Report report) {
Report report1 = reportService.addReport(report);
if(report1 == null)
return new ResponseEntity<>(report, HttpStatus.NOT_FOUND);
return new ResponseEntity<>(report1, HttpStatus.OK);
}
//Other request methods...
}
Below is the code for my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;
#Column(name = "to_user_email")
private String toUserEmail;
#Column(name = "from_user_email")
private String fromUserEmail;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
#CreatedDate
private Date createdAt;
#Column(nullable = false)
private String observation;
#Column(nullable = false)
private String context;
private String recommendation;
#Column(nullable = false)
private String eventName;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
#Column(nullable = false)
private Date eventDate;
private boolean isAnonymous;
#Temporal(TemporalType.DATE)
#JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
private Date acknowledgementDate;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Action.class)
#JoinColumn(name = "report_id")
private List<Action> actionList;
#Value("${some.key:0}")
private int rating; //Range 0 to 4
private int type;
/*
Getter and setter methods...
*/
}
I want to know if reportRepository.getOne(Long id) returns null so that I can actually check if a particular report doesn't exist in the database. If not, how else can I implement the above?
The JpaRepository.getOne with throw EntityNotFoundException if it couldn't find a record with the given id.
You can use CrudRepository.findById (JpaRepository is a subclass of CrudRepository) which will return an Optional<Report> which can be empty if there are no record for the given id. You can use Optional.isPresent() to check whether it a Report is available or not and take actions accordingly.
Create a method in your ReportRepository.
It will return Report by matched id else return null.
public Optional<Report> findById(Long id);
Note: findById(Long id); should match with the property name in your Report entity.
I am assuming your Report entity is as follows:
public class Entity{
private Long id;
...
}

UUID Mapping in hibernate

I have mapped a table to my table and trying to add some values in it. but I am getting errors as below
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'create, delete, read, role_id, update, id) values
(_binary'ØN_WlAs—\niÊnÙ' at line 1
my entities are
RoleSettings.java
#Entity #Table(name = "role_settings")
#Getter #Setter #Data
public class RoleSettings implements Serializable {
private static final long serialVersionUID = 8862104773442047690L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id", foreignKey = #ForeignKey(name = "role_settings_iam_role_FK"))
private RoleMaster roleId;
}
RoleMaster.java
#Entity #Table(name = "role")
#Getter #Setter #Data
public class RoleMaster implements Serializable {
private static final long serialVersionUID = 1792968151371176640L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#Column(name = "name", nullable = false, length = 255)
private String name;
}
RoleSettingsRepository.java
public interface RoleSettingsRepository extends JpaRepository<RoleSettings, UUID>{}
RoleSettingsService.java
#Service
Class RoleSettingsService {
#Autowired
private RoleSettingsRepository roleSettingsRepository;
public BaseDTO create(RoleSettings roleSettings) {
BaseDTO response = new BaseDTO();
RoleSettings newRoleSettings = new RoleSettings();
try {
newRoleSettings.setRoleId(roleSettings.getRoleId());
newRoleSettings.setAppAccessId(roleSettings.getAppAccessId());
newRoleSettings.setCreate(roleSettings.getCreate());
newRoleSettings.setUpdate(roleSettings.getUpdate());
newRoleSettings.setRead(roleSettings.getRead());
newRoleSettings.setDelete(roleSettings.getDelete());
roleSettingsRepository.save(newRoleSettings);
response.setStatusCode(200);
} catch (Exception e) {
}
return response;
}
}
RoleSettingsController.java
#RestController
#RequestMapping("/v1/rolesettings")
public class RoleSettingsController {
#Autowired
private RoleSettingsService roleSettingsService;
#PostMapping("/post")
public BaseDTO create(#RequestBody RoleSettings roleSettings) {
BaseDTO response = roleSettingsService.create(roleSettings);
return response;
}
}
my json object
{ "roleId" :{"id": "b2e64c82-ab75-41d3-bb10-e9150f314807"} }
and my roleId is stored in database as type binary(16).
Check in your database data type of the id column. It has to be BINARY(16). And annotate your entity field as:
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(columnDefinition = "BINARY(16)")
private UUID id;
Note that you nned to add a column definition in this case.

Resources