Пару недель я занимался моим хобби-проектом, который, по сути, является помощником для викторины.После теста я собираюсь проверить, какие вопросы и ответы я получил правильно, введя часть текста вопроса и получив категорию (например, химия), вопросы, связанные с моей очередью поиска (например, NaCl,ДНК, pH) и ответы, связанные с этими вопросами.
Все они хранятся в базе данных MySQL следующим образом: Схема базы данных .
Когда я впервыеначал, я использовал способ EAGER для извлечения всех записей (так как их было не так много, как сегодня).Однако сегодня я считаю большую коллекцию вопросов и ответов и не могу заставить работать ленивую загрузку.
Учитывая следующий простой код:
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;
}
}
Категория EntityImpl
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>
<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>
Есть еще CategoryDaoImpl.java , QuestionDaoImpl.java и AnswerDaoImpl.java , которые просто пустыи просто расширяем AbstractGenericDao.java .
Теперь, в Main.java , я получаю список категорий из базы данных:
List<CategoryEntityImpl> categories = new CategoryDaoImpl().fetchAll();
А потом, когда яя пытаюсь перебрать их через:
categories.forEach((category) -> {
Set<QuestionEntityImpl> questions = category.getQuestions();
});
Я получаю следующее:
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
Единственный обходной путь, который я нашел до сих пор, - это длинный и не очень умно выглядящий хак.Вместо цикла сверху я должен сделать следующее:
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();
});
Я знаю, что такого рода вопросы уходят далеко-далеко, но, поверьте мне, я попробовал все, что мог найти в Интернете, иПриведенный выше пример - единственное найденное мной решение, которое работает.Может я где-то ошибаюсь?Я хотел бы сделать это, просто кажется неправильным переписывать один и тот же кусок кода снова и снова для каждого представления, которое у меня есть, и каждый раз, когда мне нужно обновить некоторые данные.
Некоторая помощьбыть оцененнымЗаранее спасибо.