Quarkus Hibernate: Entity not updated when changed value provided via method - java

I use Quarkus + Hibernate to sync data to the DB and I've noticed during testing that sometimes my entity isn't updated. I've created a minimal example adjusting the original example https://github.com/quarkusio/quarkus-quickstarts/tree/main/hibernate-orm-quickstart
Here are my adjustments:
import.sql
DROP TABLE IF EXISTS fruit CASCADE;
CREATE TABLE fruit (
fruitsSequence INT PRIMARY KEY,
name TEXT NOT NULL,
test INT
);
Fruit.java
package org.acme.hibernate.orm;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.google.common.base.Objects;
#Entity
#Table(name = "known_fruits")
public class Fruit {
#Id
#SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
#GeneratedValue(generator = "fruitsSequence")
private Integer id;
#Transient
private String name = "";
#Column(name = "test")
private Integer test = -1;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
#Access(AccessType.PROPERTY)
public String getChangedName() {
return "a" + name;
}
public String getName() {
return name;
}
public void setTest(Integer test) {
this.test = test;
}
public Integer getTest() {
return test;
}
#Column(name = "name")
#Access(AccessType.PROPERTY)
protected void setChangedName(String name) {
this.name = name.substring(1);
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
return Objects.hashCode(name, test);
}
#Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit other = (Fruit) o;
return name.equals(other.name) && test.equals(other.test);
}
return false;
}
}
Replaced the test withDBTest.java
package org.acme.hibernate.orm;
import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
#QuarkusTest
public class DBTest {
#Inject
EntityManager m_em;
#Inject
UserTransaction m_transaction;
#Test
void testUpdate() throws Exception {
Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
.then().statusCode(201).extract().as(Fruit.class);
m_transaction.begin();
Fruit db = m_em.find(Fruit.class, fruit.getId());
db.setName("Apple");
db.setTest(13);
m_transaction.commit();
db = m_em.find(Fruit.class, fruit.getId());
assertEquals(13, db.getTest(), "Unexpected test");
assertEquals("Apple", db.getName(), "Unexpected name");
}
#Test
void testUpdateLongName() throws Exception {
Fruit fruit = given().when().body(
"{\"name\" : \"PeeeeeeeeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaDBaaaaaaaaaaaaaar\"}")
.contentType("application/json").post("/fruits").then().statusCode(201).extract().as(Fruit.class);
m_transaction.begin();
Fruit db = m_em.find(Fruit.class, fruit.getId());
db.setName(fruit.getName() + "Apple");
db.setTest(13);
m_transaction.commit();
db = m_em.find(Fruit.class, fruit.getId());
assertEquals(13, db.getTest(), "Unexpected test");
assertEquals(fruit.getName() + "Apple", db.getName(), "Unexpected name");
}
#Test
void testUpdateNameOnly() throws Exception {
Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
.then().statusCode(201).extract().as(Fruit.class);
m_transaction.begin();
Fruit db = m_em.find(Fruit.class, fruit.getId());
db.setName("Apple");
m_transaction.commit();
db = m_em.find(Fruit.class, fruit.getId());
assertEquals(-1, db.getTest(), "Unexpected test");
assertEquals("Apple", db.getName(), "Unexpected name");
}
#Test
void testUpdateNameOnlyREST() throws Exception {
Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
.then().statusCode(201).extract().as(Fruit.class);
given().when().body("{\"name\" : \"Apple\"}").contentType("application/json").put("/fruits/" + fruit.getId())
.then().statusCode(200).extract().as(Fruit.class);
Fruit db = m_em.find(Fruit.class, fruit.getId());
assertEquals(-1, db.getTest(), "Unexpected test");
assertEquals("Apple", db.getName(), "Unexpected name");
}
}
What I see is that testUpdateNameOnly and testUpdateNameOnlyREST fail whereas the other tests run as expected. In my original testcase, even changing another field didn't update the TEXT field hence the test with the long name. The reason why the name is altered when writing it to the DB is to encrypt it (the outcome is a BASE64 string).
I am not sure if this is a configuration issue or an actual bug.
Thanks for helps in advance!

As suggested by the comments this seems to be a bug (https://github.com/quarkusio/quarkus/issues/16619).
It works with the AttributeConver:
Fruit.java:
package org.acme.hibernate.orm;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import com.google.common.base.Objects;
#Entity
#Table(name = "known_fruits")
public class Fruit {
#Id
#SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
#GeneratedValue(generator = "fruitsSequence")
private Integer id;
#Column(name = "name")
#Convert(converter = NameConverter.class)
private String name = "";
#Column(name = "test")
private Integer test = -1;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setTest(Integer test) {
this.test = test;
}
public Integer getTest() {
return test;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
return Objects.hashCode(name, test);
}
#Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit other = (Fruit) o;
return name.equals(other.name) && test.equals(other.test);
}
return false;
}
}
NameConverter.java:
package org.acme.hibernate.orm;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter
public class NameConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String name) {
return "a" + name;
}
#Override
public String convertToEntityAttribute(String name) {
return name.substring(1);
}
}

Related

Sorting with model reference on PageRequest not working

I am using Spring data PageRequest to search data based on user filter. I need my sort column to be dynamic along with search filters.
However stateMaster.countryMaster.description, stateMaster.description, description code is not working and throws following error.
org.springframework.data.mapping.PropertyReferenceException: No property description, stateMaster found for type CountryMaster! Traversed path: DistrictMaster.stateMaster.countryMaster.
Here's my service code.
String sortColumn = filterJson.getString("sortColumn");
if ("default".equalsIgnoreCase(filterJson.getString("sortColumn"))) {
sortColumn = "stateMaster.countryMaster.description, stateMaster.description, description";
} else {
sortColumn = "description";
}
String sortOrder = filterJson.getString("sortOrder");
Integer maxResult = Integer.parseInt(hbsService.getGeneralConfigValue("adminmaxallowedlisting"));
PageRequest pageRequest = new PageRequest(pageNo, maxResult, Sort.Direction.valueOf(sortOrder), sortColumn);
Page<DistrictMaster> page = districtRepository.findAll(new Specification<DistrictMaster>() {
#Override
public Predicate toPredicate(Root<DistrictMaster> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if (filterJson.optLong("stateId") != 0) {
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("stateMaster"), filterJson.optLong("stateId"))));
}
if (!filterJson.optString("status").trim().isEmpty() && !filterJson.optString("status").equals("All")) {
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("active"), filterJson.optBoolean("status"))));
}
if (!filterJson.optString("value").trim().isEmpty()) {
predicates.add(criteriaBuilder.and(criteriaBuilder.like(criteriaBuilder.lower(root.get("description")), "%" + filterJson.optString("value").toLowerCase() + "%")));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
}, pageRequest);
Here's my model:
package com.agrisk.data.model;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
#Entity
#Table(name = "tbldistrictmst")
#SequenceGenerator(name = "districtmstseq", allocationSize = 1, sequenceName = "districtmstseq")
public class DistrictMaster extends MakerCheckerBO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5461420398663313529L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "districtmstseq")
#Column(name = "id")
public Long id;
#Column(name = "description")
public String description;
#Column(name = "code")
public String code;
#ManyToOne
#JoinColumn(name = "stateid")
private StateMaster stateMaster;
#Column(name = "active")
public Boolean active;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "districtid")
public Set<TehsilMaster> tehsilMaster;
#Column(name = "villagereferenceid")
private Long villageReferenceId;
public Set<TehsilMaster> getTehsilMaster() {
return tehsilMaster;
}
public void setTehsilMaster(Set<TehsilMaster> tehsilMaster) {
this.tehsilMaster = tehsilMaster;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public StateMaster getStateMaster() {
return stateMaster;
}
public void setStateMaster(StateMaster stateMaster) {
this.stateMaster = stateMaster;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public Long getVillageReferenceId() {
return villageReferenceId;
}
public void setVillageReferenceId(Long villageReferenceId) {
this.villageReferenceId = villageReferenceId;
}
}

How inject EntityManager to DAO or Servlet using JPA Hubernate

When I calling method onGet in Servlet I got NullPointerException. I checked it and method in DAO is correct implemented. Where should I inject entityManagaer object and how ? In DAO ? Or in Servlet ? I was trying do it with annotations but it doesn't work.
In method onGet I calling method getAllReservations() from ReservationDAO class and that method cause Nullpointerexception. I want retrieve data from my database. This method should return all data from dB what I want get. And how to fix it?
Database store two tables which contains two Reservation objects and two Person objects
My code below:
ReservationServlet class
package servlets;
import DAO.ReservationDAO;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import model.Reservation;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
#WebServlet("reservation")
public class ReservationServlet extends HttpServlet {
ReservationDAO reservationDAO = new ReservationDAO();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Reservation> reservationsList = reservationDAO.getAllReservations();
System.out.println("reservationsList size: " + reservationsList.size());
//String json = gson.toJson(reservationsList);
response.getWriter().write(reservationsList.get(0).getPerson().getName());
}
}
ReservationDAO class
package DAO;
import model.Reservation;
import javax.persistence.EntityManager;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.List;
public class ReservationDAO {
#PersistenceUnit(unitName="restapi")
EntityManagerFactory emf = Persistence.createEntityManagerFactory("restapi");
#PersistenceContext(unitName="restapi")
EntityManager em;
#Transactional
public List<Reservation> getAllReservations() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Reservation> criteriaQuery = builder.createQuery(Reservation.class);
Root<Reservation> employee = criteriaQuery.from(Reservation.class);
criteriaQuery.select(employee);
TypedQuery<Reservation> query = em.createQuery(criteriaQuery);
return query.getResultList();
}
public Reservation getReservationById(EntityManager em, int id) {
Query query = em.createQuery("FROM Reservation WHERE id =" + id);
return (Reservation) query.getSingleResult();
}
public void updateReservation(EntityManager em, int id, Reservation reservation) {
Query query = em.createQuery("UPDATE Reservation SET table_number = :tn, start_time = :st, end_time = :et WHERE id = :id");
query.setParameter("tn", reservation.getTableNumber()).setParameter("st", reservation.getStartTime())
.setParameter("et", reservation.getEndTime()).setParameter("id", reservation.getId()).executeUpdate();
}
public void removeReservation(EntityManager em, int id) {
Query query = em.createQuery("DELETE FROM Reservation WHERE id = :id");
query.setParameter("id", id).executeUpdate();
}
public void createReservation(EntityManager em, Reservation reservation) {
em.persist(reservation);
}
}
Reservation model class
package model;
import javax.persistence.*;
#Entity
public class Reservation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "table_number", columnDefinition = "VARCHAR(30) NOT NULL")
private int tableNumber;
#Column(name = "start_time" ,columnDefinition = "VARCHAR(20) NOT NULL")
private String startTime;
#Column(name = "end_time", columnDefinition = "VARCHAR(20) NOT NULL")
private String endTime;
#ManyToOne
private Person person;
public Reservation() {
}
public Reservation(int tableNumber, String startTime, String endTime, Person person) {
this.tableNumber = tableNumber;
this.startTime = startTime;
this.endTime = endTime;
this.person = person;
}
public int getTableNumber() {
return tableNumber;
}
public void setTableNumber(int tableNumber) {
this.tableNumber = tableNumber;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public long getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
Person model class
package model;
import javax.persistence.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "full_name")
private String name;
#Column(name = "phone_number")
private String phoneNumber;
private String email;
#OneToMany(mappedBy = "person")
private List<Reservation> reservationList;
public Person() {
}
public Person(String name, String phoneNumber, String email, List<Reservation> reservationList) {
this.name = name;
this.phoneNumber = phoneNumber;
this.email = email;
this.reservationList = reservationList;
}
public long getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Reservation> getReservationList() {
return reservationList;
}
public void setReservationList(List<Reservation> reservationList) {
this.reservationList = reservationList;
}
}
App class - what I use to run application by Hibernate
import DAO.ReservationDAO;
import model.Person;
import model.Reservation;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.ArrayList;
import java.util.List;
public class App {
public void populateDb(EntityManager em) {
EntityTransaction transaction = em.getTransaction();
List<Reservation> reservationList = new ArrayList<>();
Person person1 = new Person("John Smith", "458-321-842", "sgfhvdsg#gmail.com", reservationList);
Reservation reservation1 = new Reservation(37, "12.37", "13.45", person1);
reservationList.add(reservation1);
Person person2 = new Person("Gonzalez", "123-456-789", "dsafi#gmail.com", reservationList);
Reservation reservation2 = new Reservation(24, "14.23", "16.22", person2);
reservationList.add(reservation2);
transaction.begin();
em.persist(person1);
em.persist(reservation1);
em.persist(person2);
em.persist(reservation2);
transaction.commit();
}
public static void main(String[] args) {
App app = new App();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("restapi");
EntityManager em = emf.createEntityManager();
app.populateDb(em);
ReservationDAO reservationDAO = new ReservationDAO();
List<Reservation> allReservations = reservationDAO.getAllReservations();
System.out.println("allReservations size: " + allReservations.size());
em.clear();
em.close();
emf.close();
}
}
HTTP ERROR 500
Problem accessing /reservation. Reason:
Server Error
Caused by:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at servlets.ReservationServlet.doGet(ReservationServlet.java:30)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
in doget method List<Reservation> reservationsList you getting is null. what you can do
if ( reservationsList !=null && !reservationsList.isEmpty())
{
System.out.println("reservationsList size: " + reservationsList.size());
//String json =gson.toJson(reservationsList);
response.getWriter().write(reservationsList.get(0).getPerson().getName()); }
else {
response.getWriter().write("record not found")
}

How to enum mapping with jpa Spring boot why not save enum value in DB in DB saving enum key

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import org.hibernate.annotations.GenericGenerator;
import com.lue.billingsystem.enums.Status;
import com.lue.billingsystem.enums.Types;
#Entity
#Table(name="product_tab")
public class Product implements Serializable{
private static final long serialVersionUID = 8919320309645697466L;
#Id
#Column(name="prod_id",updatable=false)
#GenericGenerator(name="product_tab_genetator",strategy="increment")
#GeneratedValue(generator="product_tab_genetator")
private Long id;
private String name;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private Types type;
#Column(name = "status")
#Enumerated(EnumType.STRING)
private Status status;
#Column(name = "description", length = 200)
private String description;
#OneToMany(mappedBy="product")
private List<Charge> charges;
#Column(name = "create_date", columnDefinition = "DATETIME")
private Date createDate;
#Column(name = "update_date", columnDefinition = "DATETIME")
private Date updateDate;
//#Version
private Integer version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Types getType() {
return type;
}
public void setType(Types type) {
this.type = type;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Charge> getCharges() {
return charges;
}
public void setCharges(List<Charge> charges) {
this.charges = charges;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
import org.springframework.http.HttpStatus;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.lue.billingsystem.enums.utils.StatusDeserializer;
import com.lue.billingsystem.exception.BillingException;
#JsonDeserialize(using = StatusDeserializer.class)
public enum Status {
ACTIVE("Active"), INACTIVE("Inactive");
private final String text;
Status(final String text) {
this.text = text;
}
#Override
public String toString() {
return text;
}
public String getText() {
return this.text;
}
public static Status fromText(String text) {
for (Status r : Status.values()) {
if (r.getText().equals(text)) {
System.out.println(r);
return r;
}
}
throw new BillingException("Your Status not valied: "+text +" ", HttpStatus.BAD_REQUEST, 400);
}
}
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.lue.billingsystem.enums.Status;
public class StatusDeserializer extends JsonDeserializer<Status> {
#Override
public Status deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
if (node == null) {
return null;
}
String text = node.textValue(); // gives "A" from the request
if (text == null) {
return null;
}
//System.out.println(Status.fromText(text) + "---------------");
return Status.fromText(text);
}
}
How to enum mapping with jpa Spring boot why not save enum value in DB in DB saving enum key when i saving product in databse not save status like Active it always save ACTIVE
You just need to pay attention to the enum values when the table is being created.
What are the enum values in the table e.g. in status column, are the values defined as 'Active', 'Inactive' or 'ACTIVE', 'INACTIVE'. That's what will determine the value saved.
If the enum values are defined as 'ACTIVE', 'INACTIVE', if you insert 'active' as the value for status, it will change to 'ACTIVE' inside the database because it inserts based on the pre defined enum values.

Hibernate Lazy-Loading in a Swing Environment

I've been going on a hobby project of mine for a couple of weeks that is basically a quiz assistant. What it does is after a test, I'm going to check what questions and answers I got right by inputting part of the question text and retrieving the category (e.g.: Chemistry), the questions related to my search queue (e.g.: NaCl, DNA, pH) and the answers related to those questions.
All of them are stored in a MySQL database, in this manner: Database Scheme.
When I first started, I was using an EAGER way of fetching all the records (since there weren't as many as today). However, today I'm counting a large collection of questions and answers and can't seem to get the lazy loading to be working.
Considering the following simple code:
Main.java
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.mybizname.quizassistant.database.dao.impl.CategoryDaoImpl;
import org.mybizname.quizassistant.database.entity.impl.CategoryEntityImpl;
import org.mybizname.quizassistant.database.entity.impl.QuestionEntityImpl;
import org.mybizname.quizassistant.database.utilities.HibernateUtil;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
List<CategoryEntityImpl> categories = new CategoryDaoImpl().fetchAll();
categories.forEach((category) -> {
Set<QuestionEntityImpl> questions = category.getQuestions();
});
HibernateUtil.shutdown();
System.exit(0);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
}
HibernateUtil.java
import java.util.HashMap;
import java.util.Map;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;
import org.mybizname.quizassistant.database.entity.impl.AnswerEntityImpl;
import org.mybizname.quizassistant.database.entity.impl.CategoryEntityImpl;
import org.mybizname.quizassistant.database.entity.impl.QuestionEntityImpl;
public class HibernateUtil {
private static StandardServiceRegistry standardServiceRegistry;
private static SessionFactory sessionFactory;
static {
StandardServiceRegistryBuilder standardServiceRegistryBuilder = new StandardServiceRegistryBuilder();
Map<String, String> hibernateConfiguration = new HashMap<>();
hibernateConfiguration.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
hibernateConfiguration.put(Environment.DRIVER, "com.mysql.jdbc.Driver");
hibernateConfiguration.put(Environment.URL, "jdbc:mysql://localhost:3306/quiz_assistant");
hibernateConfiguration.put(Environment.USER, "root");
hibernateConfiguration.put(Environment.PASS, "pwd");
hibernateConfiguration.put(Environment.QUERY_TRANSLATOR, "org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory");
hibernateConfiguration.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "org.hibernate.context.internal.ThreadLocalSessionContext");
hibernateConfiguration.put(Environment.SHOW_SQL, "true");
hibernateConfiguration.put(Environment.FORMAT_SQL, "true");
standardServiceRegistryBuilder.applySettings(hibernateConfiguration);
standardServiceRegistry = standardServiceRegistryBuilder.build();
MetadataSources metadataSources = new MetadataSources(standardServiceRegistry);
metadataSources.addAnnotatedClass(CategoryEntityImpl.class);
metadataSources.addAnnotatedClass(QuestionEntityImpl.class);
metadataSources.addAnnotatedClass(AnswerEntityImpl.class);
Metadata metadata = metadataSources.getMetadataBuilder().build();
sessionFactory = metadata.getSessionFactoryBuilder().build();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session openSession() {
return sessionFactory.openSession();
}
public static Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public static void shutdown() {
if (sessionFactory != null) {
StandardServiceRegistryBuilder.destroy(standardServiceRegistry);
}
}
}
GenericEntity.java
import java.io.Serializable;
import java.util.Date;
public interface GenericEntity extends Serializable {
public Long getId();
public void setId(Long id);
public Date getRegistrationTimestamp();
public void setRegistrationTimestamp(Date registrationTimestamp);
}
AbstractGenericEntity.java
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
public abstract class AbstractGenericEntity implements GenericEntity {
#Override
abstract public Long getId();
#Override
abstract public void setId(Long id);
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "registrationTimestamp", length = 19)
protected Date registrationTimestamp;
#Override
public Date getRegistrationTimestamp() {
return this.registrationTimestamp;
}
#Override
public void setRegistrationTimestamp(Date registrationTimestamp) {
this.registrationTimestamp = registrationTimestamp;
}
}
GenericDao.java
import java.util.List;
public interface GenericDao<T> {
public void persist(T entity);
public void update(T entity);
public void remove(T entity);
public void removeAll();
public List<T> fetchAll();
public List<T> fetchAllByExample(T entity);
public T findById(Long id);
public Class<T> getType();
}
AbstractGenericDao.java
import java.lang.reflect.ParameterizedType;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.mybizname.quizassistant.database.utilities.HibernateUtil;
public abstract class AbstractGenericDao<T> implements GenericDao<T> {
private final Class<T> type;
public AbstractGenericDao() {
type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
#Override
public void persist(T entity) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void update(T entity) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void remove(T entity) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void removeAll() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public List<T> fetchAll() {
List<T> entities;
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(getType());
criteriaQuery.from(getType());
entities = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
return entities;
}
#Override
public List<T> fetchAllByExample(T entity) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public T findById(Long id) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public Class<T> getType() {
return type;
}
}
CategoryEntityImpl
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mybizname.quizassistant.database.entity.AbstractGenericEntity;
#Entity
#Table(name = "category", catalog = "quiz_assistant", uniqueConstraints = #UniqueConstraint(columnNames = "name"))
public class CategoryEntityImpl extends AbstractGenericEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#Column(name = "name", unique = true, nullable = false)
private String name;
#Column(name = "shortDescription")
private String shortDescription;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
private Set<QuestionEntityImpl> questions = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
private Set<AnswerEntityImpl> answers = new HashSet<>();
public CategoryEntityImpl() {
}
public CategoryEntityImpl(String name, Date registrationTimestamp) {
this.name = name;
this.registrationTimestamp = registrationTimestamp;
}
public CategoryEntityImpl(String name, String shortDescription, Date registrationTimestamp, Set<QuestionEntityImpl> questions, Set<AnswerEntityImpl> answers) {
this.name = name;
this.shortDescription = shortDescription;
this.registrationTimestamp = registrationTimestamp;
this.questions = questions;
this.answers = answers;
}
#Override
public Long getId() {
return this.id;
}
#Override
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getShortDescription() {
return this.shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public Set<QuestionEntityImpl> getQuestions() {
return this.questions;
}
public void setQuestions(Set<QuestionEntityImpl> questions) {
this.questions = questions;
}
public Set<AnswerEntityImpl> getAnswers() {
return this.answers;
}
public void setAnswers(Set<AnswerEntityImpl> answers) {
this.answers = answers;
}
}
QuestionEntityImpl
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.mybizname.quizassistant.database.entity.AbstractGenericEntity;
#Entity
#Table(name = "question", catalog = "quiz_assistant")
public class QuestionEntityImpl extends AbstractGenericEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "categoryId", nullable = false)
private CategoryEntityImpl category;
#Column(name = "identifier", nullable = false)
private String identifier;
#Column(name = "text", nullable = false)
private String text;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "question")
private Set<AnswerEntityImpl> answers = new HashSet<>();
public QuestionEntityImpl() {
}
public QuestionEntityImpl(CategoryEntityImpl category, String identifier, String text, Date registrationTimestamp) {
this.category = category;
this.identifier = identifier;
this.text = text;
this.registrationTimestamp = registrationTimestamp;
}
public QuestionEntityImpl(CategoryEntityImpl category, String identifier, String text, Date registrationTimestamp, Set<AnswerEntityImpl> answers) {
this.category = category;
this.identifier = identifier;
this.text = text;
this.registrationTimestamp = registrationTimestamp;
this.answers = answers;
}
#Override
public Long getId() {
return this.id;
}
#Override
public void setId(Long id) {
this.id = id;
}
public CategoryEntityImpl getCategory() {
return this.category;
}
public void setCategory(CategoryEntityImpl category) {
this.category = category;
}
public String getIdentifier() {
return this.identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public Set<AnswerEntityImpl> getAnswers() {
return this.answers;
}
public void setAnswers(Set<AnswerEntityImpl> answers) {
this.answers = answers;
}
}
AnswerEntityImpl
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.mybizname.quizassistant.database.entity.AbstractGenericEntity;
#Entity
#Table(name = "answer", catalog = "quiz_assistant")
public class AnswerEntityImpl extends AbstractGenericEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "categoryId", nullable = false)
private CategoryEntityImpl category;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "questionId", nullable = false)
private QuestionEntityImpl question;
#Column(name = "identifier", nullable = false)
private String identifier;
#Column(name = "text", nullable = false)
private String text;
public AnswerEntityImpl() {
}
public AnswerEntityImpl(CategoryEntityImpl category, QuestionEntityImpl question, String identifier, String text, Date registrationTimestamp) {
this.category = category;
this.question = question;
this.identifier = identifier;
this.text = text;
this.registrationTimestamp = registrationTimestamp;
}
#Override
public Long getId() {
return this.id;
}
#Override
public void setId(Long id) {
this.id = id;
}
public CategoryEntityImpl getCategory() {
return this.category;
}
public void setCategory(CategoryEntityImpl category) {
this.category = category;
}
public QuestionEntityImpl getQuestion() {
return this.question;
}
public void setQuestion(QuestionEntityImpl question) {
this.question = question;
}
public String getIdentifier() {
return this.identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
}
POM Dependencies:
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
</dependencies>
There are still CategoryDaoImpl.java, QuestionDaoImpl.java and AnswerDaoImpl.java which are just empty and simply extend AbstractGenericDao.java.
Now, in Main.java, I retrieve the list of categories from the database:
List<CategoryEntityImpl> categories = new CategoryDaoImpl().fetchAll();
And then, when I am trying to loop thru them via:
categories.forEach((category) -> {
Set<QuestionEntityImpl> questions = category.getQuestions();
});
I get the following:
Exception in thread "AWT-EventQueue-0" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mybizname.quizassistant.database.entity.impl.CategoryEntityImpl.questions, could not initialize proxy - no Session
The only workaround I found so far is a long and not so clever looking hack. Instead of the loop from above, I have to do this:
categories.forEach((category) -> {
System.out.println(category.getName());
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
session.lock(category, LockMode.NONE);
Set<QuestionEntityImpl> questions = category.getQuestions();
questions.forEach((question) -> {
System.out.println("\t" + question.getText());
});
tx.commit();
});
I know this kind of questions go far, far back, but trust me that I have tried anything I could find on the web and the above example is the only solution I found that works. Maybe I am mistaken somewhere? I would do that, just that it doesn't seem right to have to rewrite the same piece of code over and over again for every view I have and every time I need to refresh some data.
Some help would be appreciated.
Thank you in advance.

EclipseLink EntityManager SQL JOIN on two tables

I have 2 different tables: subjects and questions and I need to make SQL JOIN on these 2 tables. Table subjects has its attributes: name and shortcut. Table questions has its attributes: question_number, text, subject - in fact, subject from table questions is a shortcut of a subject.
I tried something like this, what I saw in one stackoverflow topic:
Query q = em.createNativeQuery("SELECT q.question_number, q.text, s.name, s.shortcut FROM "
+ "( questions q INNER JOIN subjects s ON q.subject=s.shortcut );", QuestionSubject.class);
QuestionSubject.class is an #Entity class and has attributes of both questions table and subjects table. After calling this method I saw that a new table with a name QUESTIONSUBJECT was created in my database and that is what I do not want to be done.
Can anyone help me with other solution?
P.S.: I am doing this in order to use the ouput as a response on HTTP request so I need to gather those two into one. I need to return either a List or JSON string.
EDIT: Using MySQL database.
questions table Entity class:
package model;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "questions")
#XmlRootElement
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "question_number")
private Integer questionNumber;
#Column(name = "text")
private String text;
#Column(name = "subject")
private String subject;
public Question() {
}
public Question(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public Question( String text, String subject) {
this.text = text;
this.subject = subject;
}
public Question(Integer questionNumber, String text, String subject) {
this.questionNumber = questionNumber;
this.text = text;
this.subject = subject;
}
public Integer getQuestionNumber() {
return questionNumber;
}
public void setQuestionNumber(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public int hashCode() {
int hash = 0;
hash += (questionNumber != null ? questionNumber.hashCode() : 0);
return hash;
}
#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 Question)) {
return false;
}
Question other = (Question) object;
if ((this.questionNumber == null && other.questionNumber != null) || (this.questionNumber != null && !this.questionNumber.equals(other.questionNumber))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rest.Questions[ questionNumber=" + questionNumber + " ]";
}
}
subjects table Entity class.
package model;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "subjects")
#XmlRootElement
public class Subject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 5)
#Column(name = "shortcut")
private String shortcut;
#Basic(optional = false)
#NotNull
#Lob
#Size(min = 1, max = 65535)
#Column(name = "name")
private String name;
public Subject() {
}
public Subject(String shortcut) {
this.shortcut = shortcut;
}
public Subject(String shortcut, String name) {
this.shortcut = shortcut;
this.name = name;
}
public String getShortcut() {
return shortcut;
}
public void setShortcut(String shortcut) {
this.shortcut = shortcut;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
int hash = 0;
hash += (shortcut != null ? shortcut.hashCode() : 0);
return hash;
}
#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 Subject)) {
return false;
}
Subject other = (Subject) object;
if ((this.shortcut == null && other.shortcut != null) || (this.shortcut != null && !this.shortcut.equals(other.shortcut))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rest.Subjects[ shortcut=" + shortcut + " ]";
}
}
QuestionSubject Entity class:
package model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class QuestionSubject implements Serializable
{
#Id
#Column(name = "question_number")
private Integer questionNumber;
#Column(name = "text")
private String text;
#Column(name = "shortcut")
private String shortcut;
#Column(name = "name")
private String name;
public Integer getQuestionNumber() {
return questionNumber;
}
public void setQuestionNumber(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getShortcut() {
return shortcut;
}
public void setShortcut(String shortcut) {
this.shortcut = shortcut;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The table gets created because you define a class named QuestionSubject annotated as #Entity. Per default the table name is the class name.
You could override the name like you did in Subjects with #Table(name = "subjects")
Nearly the same would happen if you would define a #ManyToMany mapping on related fields between classes Question and Subject without defining QuestionSubject class at all.
I would recommend to take a look here to get more information:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/ManyToMany
Edit
If you need a manyToMany mapping you need this table. Otherwise you can only have an oneToMany resp. manyToOne relation (using foreign keys).

Categories