JPA merge does insert new entity instead of update existing one - jpa

It seems like that when I update my existing entity using jpa merge, it does insert entity with same id instead of updating existing one expected. Because after insertion, database row order lost. But I still have same entities with same ids, does Jpa use insertion to update? I mean does it delete existing entity and insert again with updated value to do its update job. Main chaos is database order is lost then.
Here is my listener method: userService Is EJB class within I use JPA.
public void onEditUserOrganization(RowEditEvent event){
UserOrganization uorg =(UserOrganization) event.getObject();
try {
userService.updateUserOrganization(uorg);
} catch (UserException ex) {
ex.printStackTrace();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("error)"));
}finally{
parentlist = getUserOrganizationParents();
branchlist = getUserOrganizationBranches();
}
}
Here is my main update method
#Override
public void updateUserOrganization(UserOrganization org) throws UserException{
if(org != null && !em.contains(org)){
try{
UserOrganization existing = em.find(UserOrganization.class, org.getUorgId());
existing.setOrgcode(org.getOrgcode());
existing.setOrgname(org.getOrgname());
existing.setParent(org.getParent());
}catch(Exception e){
throw new UserException("Couldn't update user org with id " + org.getUorgId());
}
}
}
Here is my entity class:
public class UserOrganization implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(name = "uorg_id", nullable = false)
private Integer uorgId;
#Basic(optional = false)
#NotNull
#Column(name = "parent", nullable = false)
private short parent;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#Column(name = "orgcode", nullable = false, length = 10)
private String orgcode;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 150)
#Column(name = "orgname", nullable = false, length = 150)
private String orgname;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "uorgId")
private List<refMain> refList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "uorgId")
private List<User1> user1List;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOrganization")
private List<Bankrelation> relationList;
public UserOrganization() {
}
//getter setters..
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof UserOrganization)) {
return false;
}
UserOrganization other = (UserOrganization) object;
if ((this.uorgId == null && other.uorgId != null) || (this.uorgId != null && !this.uorgId.equals(other.uorgId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "mn.bs.crasmon.model.UserOrganization[ uorgId=" + uorgId + " ]";
}
What is the best way to do update In JPA

Related

List object mapped as EAGER is being fetched as LAZY - migrating Spring + Hibernate from 4 to 5 throwing org.hibernate.LazyInitializationException

Spring version: 5.3.19
Hibernate: 5.4.24.Final
The problem: When trying to get the List compartments from class CriticalFlight after ScrollableData gets Cleaned up #Cleanup, the list is empty since fetch was never executed.
Custom class ScrollableData execution snipped code:
List<ENTITY> filteredEntities;
#Cleanup ScrollableData<ENTITY> scrollableData =
getScrollableData(
filter,
myMarketChecker,
additionalFilters,
staticPredicateBuilders);
filteredEntities = scrollableData.getAll();
return filteredEntities;
I loop into the list returned and try to access List compartments
then got: "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.pros.travel.services.oandd.optimizer.alerts.entity.CriticalFlight.compartments, could not initialize proxy - no Session
"
Classes
Embeddable Class: CriticalFlightKey
#Data
#Embeddable
public class CriticalFlightKey implements Serializable
{
#DTOMapping(CriticalFlightDTO.FIELD_FLIGHTDATE)
#Convert(converter = DateToLocalDateAttributeConverter.class)
#Column(name = "FLTDATE", nullable = false)
private LocalDate flightDate;
#DTOMapping(CriticalFlightDTO.FIELD_DIM_CRRCODE)
#Column(name = "CRRCODE", nullable = false)
private String carrierCode;
#DTOMapping(CriticalFlightDTO.FIELD_DIM_FLTNUM)
#Column(name = "FLTNUM", nullable = false)
private String flightNumber;
#DTOMapping(CriticalFlightDTO.FIELD_DIM_ORGN)
#Column(name = "ORGN", nullable = false)
private String origin;
#DTOMapping(CriticalFlightDTO.FIELD_DIM_DSTN)
#Column(name = "DSTN", nullable = false)
private String destination;
}
Parent Class: CriticalFlight
#Data
#EqualsAndHashCode(of = {"id"})
#Entity
#Table(name = "OD_CRITICAL_FLIGHTS")
public class CriticalFlight implements
{
#JsonUnwrapped
#EmbeddedId
#DTOMapped
public CriticalFlightKey id;
...
....
#JsonManagedReference
#OneToMany(fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#JoinColumns({
#JoinColumn(name="FLTDATE", referencedColumnName="FLTDATE"),
#JoinColumn(name="CRRCODE", referencedColumnName="CRRCODE"),
#JoinColumn(name="FLTNUM", referencedColumnName="FLTNUM"),
#JoinColumn(name="ORGN", referencedColumnName="ORGN"),
#JoinColumn(name="DSTN", referencedColumnName="DSTN")
})
private List<CriticalFlightCmp> compartments = new ArrayList<>();
}
Embeddable class for child: CriticalFlightCmpKey
#Data
#Embeddable
public class CriticalFlightCmpKey implements Serializable
{
#Convert(converter = DateToLocalDateAttributeConverter.class)
#Column(name = "FLTDATE", nullable = false)
private LocalDate flightDate;
#Column(name = "CRRCODE", nullable = false)
private String carrierCode;
#Column(name = "FLTNUM", nullable = false)
private String flightNumber;
#Column(name = "ORGN", nullable = false)
private String origin;
#Column(name = "DSTN", nullable = false)
private String destination;
#Column(name = "CMPCODE", nullable = false)
private String cmpCode;
}
Child Class: CriticalFlightCmp
#Data
#EqualsAndHashCode(of = {"id"})
#Entity
#Table(name = "OD_CRITICAL_FLIGHTS_CMP")
public class CriticalFlightCmp implements IPersistable<CriticalFlightCmpKey>
{
#EmbeddedId
private CriticalFlightCmpKey id;
..
...
}
Custom class ScrollableData which uses org.hibernate.ScrollableResults to execute the query
#Slf4j
public class ScrollableData<ENTITY extends IPersistable> implements Closeable
{
private static final int SCROLLABLE_FETCH_SIZE = 10000;
private final Class<ENTITY> entityClass;
private final ScrollableResults results;
private final EntityManager entityManager;
private final List<IScrollableFilter<ENTITY>> filters = new ArrayList<>();
public ScrollableData(
Class<ENTITY> entityClass,
ScrollableResults results,
EntityManager entityManager)
{
this.entityClass = entityClass;
this.results = results;
this.entityManager = entityManager;
}
/**
* Create scrollable data from a query and entity manager session.
*
* #param entityManager Entity manager from which the query was built from.
* #param query Query to scroll on.
* #return Scrollable data
*/
static <ENTITY extends IPersistable> ScrollableData<ENTITY> fromQuery(
Class<ENTITY> entityClass,
EntityManager entityManager,
CriteriaQuery query)
{
ScrollableResults results = entityManager.createQuery(query)
.unwrap(Query.class)
.setReadOnly(true)
.setFetchSize(SCROLLABLE_FETCH_SIZE)
.setCacheable(false)
.scroll(ScrollMode.FORWARD_ONLY);
return new ScrollableData<>(entityClass, results, entityManager);
}
public List<ENTITY> getAll()
{
List<ENTITY> allEntities = new ArrayList<>();
while (next())
{
allEntities.add(get());
}
return allEntities;
}
/**
* Clears the hibernate session of any entities it's caching.
*/
public void clearSession()
{
log.debug("Clearing Session for {}", entityClass.getSimpleName());
Session hibernateSession = entityManager.unwrap(Session.class);
hibernateSession.clear();
}
/**
* Closes the scrollable results and the session contained in the entity manager.
*/
public void close()
{
clearSession();
if (results != null)
{
log.debug("Closing ScrollableResults for {}",
entityClass.getSimpleName());
results.close();
}
if (entityManager != null)
{
log.debug("Clearing EntityManager for {}", entityClass.getSimpleName());
entityManager.close();
}
}
}

Duplicate key value violates unique constraint "x_pkey"

We are facing Unique constraint violation issue when concurrent clients are trying to insert data into child table.
Suppose We have tables below
1.Users
user_id, first_name, last_name.
2.Projects
project_id project_name and project_description.
And both are having many-to-many relation ship.
When two clients are trying to create a new users. Suppose client1 created user1(id=aa1),child record project(id=1).Client2 also created user2(id=aa2), child record is project(id=1). When two clients concurrently saving record user1, and child also inserting and while second client is trying as project(id-1) already available, it is throwing unique key constraint violation.
We are checking findby(projectx) before associating child record.
We are facing this issue rarely. We have implemented retry operation when we have an exception. Even with retry operation issue is not resolving consistently. Please guide me on this.
Exception is:
HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement.
ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (ForkJoinPool.commonPool-worker-1) ERROR: duplicate key value violates unique constraint "x_key"
We are using this sample code.
#Entity
#Table(name = "tbl_users", schema = "abcd")
public class user implements Serializable {
private static final long serialVersionUID = -8153443692098431986L;
#Id
#Column(name = "userId", nullable = false)
private String userid;
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "first_name", nullable = false)
private String firstName;
#ManyToMany(mappedBy = "projectsSet", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Project> projectsSet;
}
#Entity
#Table(name = "tbl_projects", schema = "abcd")
public class Project implements Serializable {
private static final long serialVersionUID = -8153443692098431986L;
#Id
#Column(name = "project_id", nullable = false)
private String projectId;
#Column(name = "project_Name", nullable = false)
private String projectName;
#Column(name = "projectDescription", nullable = false)
private String projectDescription;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
#JoinTable(name = "users_projects", schema = "abcd", joinColumns = {#JoinColumn(name = "_projectId")},
inverseJoinColumns = {#JoinColumn(name = "_userId")})
private Set<User> usersSet;
}
#Transactional
public void saveProject(ProjectDTO projectDTO, Set<UserDTO> userDTOSet) {
Optional<Project> optionalProject = getProjectFromDB(projectDTO.getProjectId());
List<User> existUsers = null;
if (!optionalProject.isPresent()) {
Map<String, UserDTO> userDTOMap = userDTOSet.stream().collect(Collectors.toMap(UserDTO::getUserId, userDTO -> userDTO));
if (MapUtils.isNotEmpty(userDTOMap)) {
existingUsers = getUsersFromDB(userDTOMap.keySet());
}
Project project = new Project();
try {
BeanUtils.copyProperties(project, projectDTO)
updateExistingUsers(project, userDTOMap, existingUsers);
addNewUsers(project, userDTOMap);
} catch (IllegalAccessException e) {
throw new BeanCopyException("Exception raised while copying the data", e);
} catch (InvocationTargetException e) {
throw new BeanCopyException("Exception raised while copying the data", e);
}
try {
ProjectRepository.save(user);
} catch (Exception e) {
throw new CustomException("Exception raised while saving the new Project", e);
}
}
private void updateExistingUsers (Project
newProject, Map < String, UserDTO > newUsers, List < User > existingUsers){
if (CollectionUtils.isNotEmpty(existingUsers)) {
existingUsers.stream().forEach(existinguser -> {
newProject.addNewUser(existinguser);
newUsers.remove(existinguser.getUserId());
});
}
}
private void addNewUsers(Project newProject, Map < String, UserDTO > userDTOMap){
userDTOMap.values().stream().forEach(userDTO -> {
User user = convertToModel(userDTO);
newProject.addNewUser(newUser);
});
}
You could try to use #SQLInsert to use a custom "upsert" statement for User as I have answered a few times already. See here Hibernate Transactions and Concurrency Using attachDirty (saveOrUpdate)

How to filter by faculty name?

I want to implement lazy record loading on a Primefaces DataTable (version 7). I have two entities, one is called Faculties and the other is Careers, which are related. The datatable correctly shows the list of all the races (includes pagination and filtering), the problem I have is that I do not know how to filter the races by the name of a certain faculty, since I do not know how to include the join in the query that I leave then.
Could you guide me on how to solve it please?
Entity Faculties
public class Facultades implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idfacultad")
private Integer idfacultad;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "nombre")
private String nombre;
#Size(max = 20)
#Column(name = "abreviatura")
private String abreviatura;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idfacultad")
private List<Carreras> carrerasList;}
Entity Carreras
public class Carreras implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idcarrera")
private Integer idcarrera;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 150)
#Column(name = "nombre")
private String nombre;
#Basic(optional = false)
#NotNull
#Column(name = "tipo")
private int tipo;
#JoinColumn(name = "idfacultad", referencedColumnName = "idfacultad")
#ManyToOne(optional = false)
private Facultades idfacultad;}
Query findByParams
public List<Carreras> findByParams(int start, int size, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Carreras> criteriaQuery = criteriaBuilder.createQuery(Carreras.class);
Root<Carreras> root = criteriaQuery.from(Carreras.class);
CriteriaQuery<Carreras> select = criteriaQuery.select(root);
Join<Carreras, Facultades> facultad = root.join("idfacultad");
if (sortField != null) {
criteriaQuery.orderBy(sortOrder == SortOrder.DESCENDING ? criteriaBuilder.asc(root.get(sortField)) : criteriaBuilder.desc(root.get(sortField)));
}
if (filters != null && filters.size() > 0) {
List<Predicate> predicados = new ArrayList<>();
filters.entrySet().forEach((entry) -> {
String key = entry.getKey();
Object val = entry.getValue();
if (!(val == null)) {
// Construimos la expresion con los predicados que si existan
Expression<String> expresion = root.get(key).as(String.class);
Predicate predicado = criteriaBuilder.like(criteriaBuilder.lower(expresion), "%" + val.toString().toLowerCase() + "%");
predicados.add(predicado);
}
});
if (predicados.size() > 0) {
criteriaQuery.where(criteriaBuilder.and(predicados.toArray(new Predicate[predicados.size()])));
}
}
// Creamos la consulta
TypedQuery<Carreras> consulta = em.createQuery(select);
consulta.setFirstResult(start);
consulta.setMaxResults(size);
return consulta.getResultList();
}
You need to manually check if the filter key equals the Facultades object, and in that case create a predicate on the joined expression that you have already created:
if (key.equals("Facultad")) {
expresion = facultad.get("nombre").as(String.class);
} else {
expresion = root.get(key).as(String.class);
}

JPA WHERE clause

I am asking about WHERE clause. I have searched internet but I didn`t find answer for my question.
I have a few entity classes, which i represent below:
#NamedQuery(name = "selectTrasy",
query = "SELECT t FROM Trasa t WHERE t.raport = :raport ORDER BY t.id")
#Entity
public class Trasa implements Serializable {
#Id
#Column(name = "id", unique = true )
#GeneratedValue
private Long id;
#Column(name = "data_z")
private Date dataz;
#Column(name = "data_do")
private Date datado;
#Column(name = "czas_z")
private String czasZ;
#Column(name = "czas_do")
private String czasDo;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "miejscowosc_z")
private MiejscowoscDB miejscowoscZ;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "miejscowosc_do")
private MiejscowoscDB miejscowoscDo;
#OneToOne(fetch = FetchType.LAZY)
private Uzytkownik user;
#OneToOne(fetch = FetchType.LAZY)
private Raport raport;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((Trasa) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
//next I have getters and setters
}
#Entity
public class Raport implements Serializable {
#Id
#Column(name = "id", unique = true )
#GeneratedValue
private Long id;
#Column(name = "data")
private Date data;
#Column(name = "nazwa")
private String nazwa;
#Enumerated(EnumType.STRING)
private Powod powod;
#OneToOne(fetch = FetchType.LAZY)
private Uzytkownik uzytkownik;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((Raport) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
//getters and setters
}
My question is why this query doesn`t work properly. Returned list is empty, but recordes exists in database, when I want return all Trasa records there is everything ok, only not works when I add WHERE clause with raport property.
trasy = (List<Trasa>)(manager.createNamedQuery("selectTrasy")
.setParameter("raport", propertyWydatek.getRaport())
.getResultList());
Change your query to:
#NamedQuery(name = "selectTrasy",
query = "SELECT t FROM Trasa t WHERE t.raport.id = :raportId ORDER BY t.id")
and run it like this:
trasy = (List<Trasa>)(manager.createNamedQuery("selectTrasy")
.setParameter("raportId", propertyWydatek.getRaport().getId())
.getResultList());
Other version of the query:
#NamedQuery(name = "selectTrasy",
query = "SELECT t FROM Trasa t join t.raport r WHERE r.id = :raportId ORDER BY t.id")

Bean validation succeeded but failed on jpa merge method

I want to persist an entity(MyEntity) with merge method. This entity have some beans validation.
public class MyEntity extends AbstractEntity {
#Basic(optional = false)
#Column(name = "city", length = 255, nullable = false)
#NotNull
#NotEmpty(message = "{myentity.validation.size.name}")
private String city;
private String number;
#Basic(optional = false)
#Column(name = "zipcode", length = 255, nullable = false)
#NotNull
private String zipcode;
private String phoneNumber;
#Email(message = "{myentity.validation.conform.email}")
#Size(min = 2, max = 100, message = "{myentity.validation.size.email}")
private String email;
private String website;
private String gpsLocation;
#ElementCollection()
#CollectionTable(name = "translation_poi", joinColumns = #JoinColumn(name = "point_id"))
#MapKeyJoinColumn(name = "locale")
#NotEmpty
private Map<Locale, MyEntityI18n> translations = new HashMap<>();
}
#Embeddable
public class MyEntityI18n implements java.io.Serializable {
#Basic(optional = false)
#Column(name = "name", length = 255, nullable = false)
#NotNull
#NotEmpty(message = "{myentity.validation.size.name}")
private String name;
#Column(name = "comment", length = 1200)
private String comment;
#Column(name = "short_description", length = 1200)
private String shortDescription;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The merge succeeded on an existing entity value but with a new entity the merge failed despite the fact that the following validation succeeded.
private boolean validate(MyEntity poi) {
boolean result = true;
Set<ConstraintViolation<MyEntity>> constraintViolations = validator.validate(poi);
if (constraintViolations.size() > 0) {
result = false;
for (ConstraintViolation<MyEntity> constraints : constraintViolations) {
FacesContext context = FacesContext.getCurrentInstance();
String message = constraints.getPropertyPath() + " " + constraints.getMessage();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, constraints.getMessage(), message));
}
}
return result;
}
Try to add a #Valid to MyEntity.translations property. I think that your validation method hasn't take account the MyEntityI18n.name validation.
About merge fails, Do you have a not-null DB constraint on the MyEntityI18n.name field?
Good luck!