dimanche 26 juillet 2015

Another LazyInitializationException topic... Java EE and JPA

I've stumbled upon the LazyInitializationException while trying to learn JavaEE.
There's tons of topics on SO for dealing with the famous exception, but most of them involve Spring or a Hibernate-specific approach - what I'm looking for is how to deal with it using JPA (Hibernate as a provider) and JavaEE.

Using hibernate.enable_lazy_load_no_trans works, but everywhere I look people say to use it with caution.

The Open Session In View pattern is another solution that is considered as bad practice.

I found an article:
http://ift.tt/1LM8we7
The Fetch Query solution looks good (and safe!), but the amount of queries that have to be written (especially after a application grows) is terrifying...

It just feels there must be better way of lazy loading.

I'll just provide a sample app in which I'm dealing with the exception, perhaps it may be helpful...

Entities:

@Entity
@Table(name = "author")
@NamedQueries({
        @NamedQuery(name = Author.findAll, query = "SELECT a FROM Author a"),
        @NamedQuery(name = Author.findById, query = "SELECT a FROM Author a WHERE a.id = :id"),
        @NamedQuery(name = Author.findByName, query = "SELECT a FROM Author a WHERE a.name = :name"),
        @NamedQuery(name = Author.findBySurname, query = "SELECT a FROM Author a WHERE a.surname = :surname"),
        @NamedQuery(name = Author.findByNameAndSurname, query = "SELECT a FROM Author a WHERE a.name = :name AND a.surname = :surname"),
        @NamedQuery(name = Author.findByBirthdate, query = "SELECT a FROM Author a WHERE a.birthdate = :birthdate")
})
public class Author {

    public static final String findAll = "Author.findAll";
    public static final String findById = "Author.findById";
    public static final String findByName = "Author.findByName";
    public static final String findBySurname = "Author.findBySurname";
    public static final String findByNameAndSurname = "Author.findByNameAndSurname";
    public static final String findByBirthdate = "Author.findByBirthdate";

    @Id
    @Column(name = "id", nullable = false, insertable = true, updatable = true)
    @SequenceGenerator(name = "author_id_seq", sequenceName = "author_id_seq", allocationSize = 1)
    @GeneratedValue(generator = "author_id_seq", strategy = GenerationType.SEQUENCE)
    private long id;

    @Basic
    @Column(name = "name", nullable = false, insertable = true, updatable = true, length = 50)
    private String name;

    @Basic
    @Column(name = "surname", nullable = false, insertable = true, updatable = true, length = 100)
    private String surname;

    @Basic
    @Column(name = "birthdate", nullable = true, insertable = true, updatable = true)
    private Timestamp birthdate;

    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<Book> bookList;

gettters and setters...



@Entity
@Table(name = "book")
@NamedQueries({
        @NamedQuery(name = Book.findAll, query = "SELECT b FROM Book b"),
        @NamedQuery(name = Book.findById, query = "SELECT b FROM Book b WHERE b.id = :id"),
        @NamedQuery(name = Book.findByName, query = "SELECT b FROM Book b WHERE b.name = :name"),
        @NamedQuery(name = Book.findByAuthor, query = "SELECT b FROM Book b WHERE b.author = :author"),
        @NamedQuery(name = Book.findByPublisher, query = "SELECT b FROM Book b WHERE b.publisher = :publisher")
})
public class Book {

    public static final String findAll = "Book.findAll";
    public static final String findById = "Book.findById";
    public static final String findByName = "Book.findByName";
    public static final String findByAuthor = "Book.findByAuthor";
    public static final String findByPublisher = "Book.findByPublisher";

    @Id
    @Column(name = "id", nullable = false, insertable = true, updatable = true)
    @SequenceGenerator(name = "book_id_seq", sequenceName = "book_id_seq", allocationSize = 1)
    @GeneratedValue(generator = "book_id_seq", strategy = GenerationType.SEQUENCE)
    private long id;

    @Basic
    @Column(name = "name", nullable = false, insertable = true, updatable = true, length = 200)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "author_id", referencedColumnName = "id")
    private Author author;

getters and setters...

Sample DAO:

public class DAO<T> {

    @Inject
    @Bookstore
    protected EntityManager em;

    public void persist(T object) {
        em.getTransaction().begin();
        em.persist(object);
        em.getTransaction().commit();
    }

    public void merge(T object) {
        em.getTransaction().begin();
        em.merge(object);
        em.getTransaction().commit();
    }

    public void remove(T object) {
        em.getTransaction().begin();
        em.remove(object);
        em.getTransaction().commit();
    }
}



public class AuthorDAO extends DAO<Author> {

    public Author findById(long id) {
        return (Author) em.createNamedQuery(Author.findById).setParameter("id", id).getSingleResult();
    }

    public List<Author> findAll() {
        return em.createNamedQuery(Author.findAll).getResultList();
    }

    public List<Author> findByName(String name) {
        return em.createNamedQuery(Author.findByName).setParameter("name", name).getResultList();
    }

    public List<Author> findBySurname(String surname) {
        return em.createNamedQuery(Author.findBySurname).setParameter("surname", surname).getResultList();
    }

    public List<Author> findByNameAndSurname(String name, String surname) {
        return em.createNamedQuery(Author.findByNameAndSurname).setParameter("name", name).setParameter("surname", surname).getResultList();
    }

    public List<Author> findByBirthdate(Timestamp birthdate) {
        return em.createNamedQuery(Author.findByBirthdate).setParameter("birthdate", birthdate).getResultList();
    }
}

A CDI bean from which I'm trying to retrieve the authors book list to show in a JSF page:

@Named("book")
@RequestScoped
public class BookBB {

    @Inject
    @FromContext
    private Author author;

    public List<Book> getBookList() {
        return author.getBookList();
    }

}

Simplest ever EntityManager producer:

public class EntityManagerProducer {

    @PersistenceContext(unitName = "simple-bookstore")
    private EntityManager entityManager;

    @Produces
    @Bookstore
    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void disposeEntityManager(@Disposes @Bookstore EntityManager entityManager) {
        entityManager.close();
    }
}

Last, but not least - the persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://ift.tt/1cKbVbQ" version="2.1">
    <persistence-unit name="simple-bookstore">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:jboss/datasources/PostgresqlDS</jta-data-source>
        <class>db.entity.Author</class>
        <class>db.entity.Book</class>
        <class>db.entity.Publisher</class>
        <class>db.entity.SchemaVersion</class>
        <properties>
            <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/bookstore"/>
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
            <property name="hibernate.connection.username" value="bookstore"/>
            <property name="hibernate.connection.password" value="bookstore"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

Aucun commentaire:

Enregistrer un commentaire