How to fix the most common LazyInitializationException - Hibernate JPA - java

I'm struggling with LazyInitializationException. I've read so far literally every article about that, but unfortunately I didn't find solution for my problem. Many of those solutions using EntityManager or things which I don't using. I'm connecting with my DB via JPA.
I've got couple of entities, but the problem is just with two of them: Order and OrderDetails.
When I get from DB object Order, and then I'll try to do order.getOrderDetails() then I gettin following error:
Exception:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.tradesystem.order.Order.orderDetails, could not initialize proxy - no Session
but it works the other way around: from OrderDetails object I can get Order by objectDetail.getOrder() .
What's more: I can't use Eager loading due to fact that I'm using it in another entity.
Here's my Order class:
#Entity
#Data
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Long id;
private LocalDate date;
#OneToMany(mappedBy = "order")
private List<OrderDetails> orderDetails;
#ManyToOne
#JoinColumn(name = "buyer_fk")
private Buyer buyer;
#ManyToOne
#JoinColumn(name = "supplier_fk")
private Supplier supplier;
}
OrderDao:
#Repository("orderDao")
public interface OrderDao extends JpaRepository<Order, Long> {
#Query(value = "SELECT * FROM orders " +
"WHERE MONTH(orders.date) = ?1 AND YEAR(orders.date) = ?2",
nativeQuery = true)
List<Order> getMonthOrders(int month, int year);
}
and OrderDetails:
#Entity
#Data
#Table(name = "orderDetails")
public class OrderDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_details_id")
private Long id;
private BigDecimal quantity;
private BigDecimal sum;
#ManyToOne
#JoinColumn(name = "order_fk")
private Order order;
#ManyToOne
#JoinColumn(name = "product_fk")
private Product productType;
#OneToOne
#JoinColumn(name = "order_comment_fk")
private OrderComment orderComment;
}
Relationship that finished with #Many is Lazy loaded by default, so why it doesn't load my OrderDetails when I doing .getOrderDetails() ?
I will be really gratefull for any help!
PS. I'm begginer, so if I didn't explain something enough good, don't hestitate to make some questions.

Use this
#Repository("orderDao")
public interface OrderDao extends JpaRepository<Order, Long> {
#Query(value = "SELECT orders FROM Order orders LEFT JOIN FETCH orders.orderDetails " +
"WHERE MONTH(orders.date) = ?1 AND YEAR(orders.date) = ?2")
List<Order> getMonthOrders(int month, int year);
}

I have solved that problem. I was running everything in CommandLineRunner. Lazy Exception disappeared when I started making tests insted of "trying something" in main class.
My main class looked like:
#Bean
public CommandLineRunner bookDemo(OrderDao orderDao, PriceDao priceDao, ProductDao productDao,
OrderService orderService, InvoiceDao invoiceDao, InvoiceService invoiceService) {
OrderService orderService, InvoiceDao invoiceDao, InvoiceService invoiceService,
ReportService reportService) {
return (args) -> {
List<Order> orders = orderDao.findByBuyerId(1L);
for (Order order : orders) {
orderService.payForOrde(order);
}

Related

One to many relationship supporting reads & deletes but not inserts

I would like to extend the requirements mentioned in the earlier post to support deletes. We have two data model object - Organization & Department sharing a one-to-many relationship. With the below mapping I am able to read the list of departments from the organization object. I have not added the cascade ALL property to restrict adding a department when creating an organization.
How should I modify the #OneToMany annotation (and possibly #ManyToOne) to restrict inserts of department but cascade the delete operation such that all associated departments are deleted when deleting an organization object?
#Entity
#Table(name="ORGANIZATIONS")
public class Organization{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#OneToMany(mappedBy = "organization", fetch = FetchType.EAGER)
private List<Department> departments;
}
#Entity
#Table(name="DEPARTMENTS")
Public class Department{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#ManyToOne(fetch = FetchType.EAGER)
private Organization organization;
}
The code to delete the organization is just a line
organizationRepository.deleteById(orgId);
The test case to validate this is as below
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#Transactional
public class OrganizationRepositoryTests {
#Autowired
private OrganizationRepository organizationRepository;
#Autowired
private DepartmentRepository departmentRepository;
#Test
public void testDeleteOrganization() {
final organization organization = organizationRepository.findByName(organizationName).get(); //precondition
Department d1 = new Department();
d1.setName("d1");
d1.setorganization(organization);
Department d2 = new Department();
d2.setName("d2");
d2.setorganization(organization);
departmentRepository.save(d1);
departmentRepository.save(d2);
// assertEquals(2, organizationRepository.getOne(organization.getId()).getDepartments().size()); //this assert is failing. For some reason organizations does not have a list of departments
organizationRepository.deleteById(organization.getId());
assertFalse(organizationRepository.findByName(organizationName).isPresent());
assertEquals(0, departmentRepository.findAll().size()); //no departments should be found
}
}
See code comments on why it fails:
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#Transactional
public class OrganizationRepositoryTests {
#Autowired
private OrganizationRepository organizationRepository;
#Autowired
private DepartmentRepository departmentRepository;
#PersistenceContext
private Entitymanager em;
#Test
public void testDeleteOrganization() {
Organization organization =
organizationRepository.findByName(organizationName).get();
Department d1 = new Department();
d1.setName("d1");
d1.setOrganization(organization);
Department d2 = new Department();
d2.setName("d2");
d2.setOrganization(organization);
departmentRepository.save(d1);
departmentRepository.save(d2);
// this fails because there is no trip to the database as Organization
// (the one loaded in the first line)
// already exists in the current entityManager - and you have not
// updated its list of departments.
// uncommenting the following line will trigger a reload and prove
// this to be the case: however it is not a fix for the issue.
// em.clear();
assertEquals(2,
organizationRepository.getOne(
organization.getId()).getDepartments().size());
//similary this will execute without error with the em.clear()
//statement uncommented
//however without that Hibernate knows nothing about the cascacding
//delete as there are no departments
//associated with organisation as you have not added them to the list.
organizationRepository.deleteById(organization.getId());
assertFalse(organizationRepository.findByName(organizationName).isPresent());
assertEquals(0, departmentRepository.findAll().size());
}
}
The correct fix is to ensure that the in-memory model is always maintained correctly by encapsulating add/remove/set operations and preventing
direct access to collections.
e.g.
public class Department(){
public void setOrganisation(Organisation organisation){
this.organisation = organisation;
if(! organisation.getDepartments().contains(department)){
organisation.addDepartment(department);
}
}
}
public class Organisation(){
public List<Department> getDepartments(){
return Collections.unmodifiableList(departments);
}
public void addDepartment(Department departmenmt){
departments.add(department);
if(department.getOrganisation() != this){
department.setOrganisation(this);
}
}
}
Try this code,
#OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "organisation_id", referencedColumnName = "id")
private List<Department> departments;
#ManyToOne(fetch = FetchType.EAGER,ascade = CascadeType.REFRESH,mappedBy = "departments")
private Organization organization;
if any issue inform
You can try to add to limit the cascade to delete operations only from Organization to department:
#OneToMany(mappedBy = "organization", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Department> departments;
Please note that if you have dependents/foreign key constraints on the department entity, then you would need to cascade the delete operations to these dependent entities as well.
You can read this guide, it explains the cascade operations nicely:
https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/

LazyInitializationException Spring Boot

I know there are a lot of similar threads out there but i just can't figure it out from those threads on how to overcome this problem.
I have 3 classes Car, Brand, Color.
A Car has just one Brand and a list of Colors.
Brand has a List of Cars.
Color does not have any relation.
Getters, Setters, ToString and Constructors are not provided for simplicity sake.
I'm able to save objects into database and database is already populated.
--------------------------------------------------------------------------------
#Entity
#Table(catalog = "spring_project")
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String model;
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable( name = "car_color", catalog = "spring_project",
joinColumns = { #JoinColumn(name = "car_id") },
inverseJoinColumns = { #JoinColumn(name = "colors_id") }
)
private List<Color> colors = new ArrayList<>();
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name="brand_id", referencedColumnName="id")
private Brand brand;
--------------------------------------------------------------------------------
#Entity
#Table(catalog = "spring_project")
public class Brand {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "brand", fetch = FetchType.LAZY)
private List<Car> cars = new ArrayList<>();
--------------------------------------------------------------------------------
#Entity
#Table(catalog = "spring_project")
public class Color {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
--------------------------------------------------------------------------------
Everything runs just fine if i fetch like Eager, but i know it is a bad practice and it should be used Lazy loading instead. But i keep getting the LazyInitializationException.
I understand from the error that a session is required but i dont know how to provide one since im working with Spring Data JPA neither where i should declare one...
#SpringBootApplication
public class SrpingJpaApplication {
private static final Logger log =
LoggerFactory.getLogger(SrpingJpaApplication.class);
public static void main(String[] args) {
SpringApplication.run(SrpingJpaApplication.class, args);
}
#Bean
public CommandLineRunner demo(CarRepository carRepository,
ColorRepository colorRepository,
BrandRepository brandRepository) {
return (args) -> {
log.info("Reads all cars....");
for (Car c : carRepository.findAll()) {
System.out.println(c.toString());
}
};
}
}
Thank you so much.
Edited----->>>
The error is thrown on c.toString();
Error: Caused by: org.hibernate.LazyInitializationException: could not initialize
proxy [com.readiness.moita.SrpingJPA.Models.Brand#1] - no Session
The default for the #OneToMany annotation is FetchType.LAZY so your collections are loaded lazily.
In order to be able to access the collection after you've retrieved the object you need to be in a transactional context (you need an open session)
When you call:
carRepository.findAll();
internally a new session is created, the object is retrieved and as soon as the findAll method returns the session is closed.
What you should do is make sure you have an open session whenever you access the lazy collection in your Car object (which the toString does).
The simplest way is to have another service handle the car loading and annotate the showCars method with #Transactional the method is in another service because of the way AOP proxies are handled.
#Service
public CarService {
final CarRepository carRepository;
public CarService(CarRepository carRepository) {
this.carRepository = carRepository;
}
#Transactional
public void showCars(String... args) {
for (Car c : carRepository.findAll()) {
System.out.println(c.toString());
}
}
}
and then you call:
#Bean
public CommandLineRunner demo(CarService carService) {
return (args) -> service.showCars(args);
}
Because the FetchType of Brand is lazy, it will not automatically be loaded into the session with call to fetchAll(). To have it automatically load into the session, you need to:
Change
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name="brand_id", referencedColumnName="id")
private Brand brand;
to
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
Ex
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="brand_id", referencedColumnName="id")
private Brand brand;
If you do not want to set the fetch type to eager, then you need to move your call to toString to a service method Ex
#Component
public CarService implements ICarService {
#Autowired
CarRepository carRepository;
#Transactional
public void printAllCars() {
for (Car c : carRepository.findAll()) {
System.out.println(c.toString());
}
}
}
The correct way to do this however would be to write a criteria query or hql

Spring JPA repository methods stopped working

Following are the classes and repository
Problem: First three methods of my repository are not working, first two were working fine initially but once i got error in saving my object due to missing AUTO INCREMENT in my database table,so i added that but than these methods stopped working.
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'cststudent0_.id' in 'field list'
I have tried the solution of others having same exception but nothing work also i want to understand the reason what is actually going on. I am using spring JPA with MySQL.
UPDATED: I have tried few more common methods like findOne() and findAll() they are also not working, i think the returned type with CSTStudent (i.e. my entity) is causing this exception.
#Entity
#Table(name = "course_section_teacher")
public class CourseSectionTeacher implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
private Course course;
#OneToOne
private Section section;
#OneToOne
private Teacher teacher;
//getter setter
}
#Entity
#Table(name = "cst_student")
public class CSTStudent implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
private CourseSectionTeacher courseSectionTeacher;
#OneToOne
private Student student;
//getter setter
}
public interface CSTStudentRepository extends CrudRepository<CSTStudent, Long> {
List<CSTStudent> findByStudent(Student student);
List<CSTStudent> findByStudentId(long studentId);
#Query("Select csts FROM CSTStudent csts inner join csts.student s inner join csts.courseSectionTeacher cst "
+ "where s.id = :studentid and cst.id = :cstid")
CSTStudent findUsingStudentAndCourseSectionTeacher(#Param(value="studentid") long studentid,#Param(value="cstid") long cstid);
#Query("Select csts.courseSectionTeacher FROM CSTStudent csts inner join csts.student s where s.id = :studentid")
List<CourseSectionTeacher> findStudentCourses(#Param(value="studentid") long studentid);
}

Error in filtering by child object in Spring Data JPA Query

My code structure looks like the following.
Article:
#Entity
public class NewsArticle{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
[Other class properties such as title, publisher, publishedDate, etc.]
#OneToMany(mappedBy = "article")
private Set<UserReadNewsArticle> userReadNewsArticles = new HashSet<>();
[Getters and Setters]
}
Article read by User:
#Entity
public class UserReadNewsArticle {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private Long readAccountId;
private Long readArticleId;
#JsonIgnore
#ManyToOne
private Account account;
#JsonIgnore
#ManyToOne
private NewsArticle article;
[Getters and Setters]
}
Account:
#Entity
public class Account {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
[Other class properties]
#OneToMany(mappedBy = "account")
private Set<UserReadNewsArticle> userReadNewsArticles = new HashSet<>();
[Getters and Setters]
}
I want to have a query method in my NewsArticleRepository to get all the Read News Articles for a user.
public interface NewsArticleRepository extends PagingAndSortingRepository<NewsArticle, Long>{
Collection<NewsArticle> findByUserReadNewsArticlesReadAccountId(Long readAccountId);
}
This method works great. But how can I write a Spring Data JPA Query/Method to get the "Unread News Articles for a user". What I have tried is the following.
Collection<NewsArticle> findByUserReadNewsArticlesReadAccountIdNot(Long readAccountId);
This one does return a list of articles which have been read by other users. But my requirement is to get all the unread news articles. I have gone through Spring Data JPA Documentation but failed to come up with an easier soultion. How can I overcome this issue? Or am I doing something wrong?
You could achieve your result by using a JPQL query with also a subquery:
public interface NewsArticleRepository extends PagingAndSortingRepository<NewsArticle, Long> {
#Query("SELECT n FROM NewsArticle n WHERE n NOT IN "
+ "(SELECT ur.article FROM UserReadNewsArticle ur JOIN ur.account a WHERE a.id = :readAccountId)")
Collection<NewsArticle> findByUserReadNewsArticlesReadAccountIdNotIn(#Param("readAccountId") Long readAccountId);
}
http://localhost:8080/newsArticles/search/findByUserReadNewsArticlesReadAccountIdNotIn?readAccountId=1
So first get the read articels from the current user and then exlude them from the whole article list.
I don't think that spring data is able to get you the same, since a subquery is definitetly needed. If I'm wrong, somebody can correct me.

Hibernate Criteria One to Many issues

I am trying to use Hibernate Criteria api to fetch only the topics based on the USER_ID but have no idea how to do it using the criteria.
My Tables are "topic_users" (below)
and "topics" table (below)
I know how to do it using SQL, this would be something like:
SELECT TOPICNAME
FROM topic_users INNER JOIN topics on topic_users.TOPICS_TOPICS_ID = topics.TOPICS_ID
WHERE topic_users.USER_ID = 1
This will return all TOPICNAME of USER_ID 1 which is exactly what I want but how I can do this with Hibernate Criteria. So far I have this in my Repository class (see below) but this will only return a highly nested JSON array. I could loop through the objects, use a DTO and build my response or try the Hibernate createSQLQuery method that will let me call a native SQL statement directly (haven't tried that yet)...but I am trying to learn the Criteria so I hope anyone can answer my query.
#Repository("userTopicsDao")
public class UserTopicsDaoImpl extends AbstractDao<Integer, UserTopics>implements UserTopicsDao {
#Override
public List<UserTopics> findMyTopics(int userId) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("userId", userId));
List<UserTopics> userTopicsList = (List<UserTopics>)crit.list();
return userTopicsList;
}
and my TOPIC_USERS Entity where I have mapped the TOPICS
#Entity
#Table(name="TOPIC_USERS")
public class UserTopics {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUSER_ID")
private Integer id;
#Column(name="USER_ID")
private Integer userId;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "TOPICS_ID")
private Set<Topics> topicsUser;
//getter and setters
Ok starting from the ground up.. you entity classes should look like this:
#Entity
#Table(name="TOPIC_USERS")
public class UserTopics {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUSER_ID")
private Integer id;
#Column(name="USER_ID")
private Integer userId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TOPICS_TOPICS_ID")
private Topics topics;
Your Topics class should look like this:
#Entity
#Table(name="TOPICS")
public class Topic {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUS_ID")
private Integer id;
#Column(name="TOPICNAME")
private Integer topicName;
#OneToMany(mappedBy = "topics")
private Set<UserTopics> userTopics;
Finally the Criteria:
Version 1) You get entire entity:
Criteria c = session.createCriteria(Topics.class, "topics");
c.createAlias("topics.userTopics", "userTopics");
c.add(Restrictions.eq("userTopics.userId", userId));
return c.list(); // here you return List<Topics>
Version 2) You project only the topicname:
Criteria c = session.createCriteria(Topics.class, "topics");
c.createAlias("topics.userTopics", "userTopics");
c.add(Restrictions.eq("userTopics.userId", userId));
c.setProjection(Projections.property("topics.topicName"));
List<Object[]> results = (List<Object[]>)c.list();
// Here you have to manually get the topicname from Object[] table.
}

Categories