休眠,会话,延迟加载

时间:2012-04-05 06:08:27

标签: hibernate lazy-loading hibernate-session

我正在使用Hibernate和JSF / primefaces开发Java Web应用程序。 我有时会遇到像

这样的错误

1)具有相同标识符的对象已与会话关联。

2)无法加载延迟初始化 * ,没有会话或会话已经关闭。

我知道这是由于我的应用程序编码不当造成的。 这就是我的做法:

当用户请求页面时(让它成为员工列表)。 用户将获得员工列表页面(empployeeList.xhtml) EmployeeListMBean是此页面的托管bean。 在构造函数的托管bean中,我调用了一个方法populateEmployees()。 populateEmployee()将使用EmployeeDao方法getAllEmployee()来getAllemployees。

员工类来到这里:

@Entity
@Table(name = "Employee")
@NamedQueries({
    @NamedQuery(name = "Employee.getAllEmployee", query = "from Employee"),
    @NamedQuery(name = "Employee.findEmployeeByFirstName", query = "from Employee where firstName = :firstName"),
    @NamedQuery(name = "Employee.findEmployeeByLastName", query = "from Employee where lastName = :lastName"),
    @NamedQuery(name = "Employee.findEmployeeByMiddleName", query = "from Employee where middleName = :middleName"),
    @NamedQuery(name = "Employee.findEmployeeByOffice", query = "from Employee where office.id = :officeId")
})
public class Employee implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "EID")
    private long id;
    @Column(name = "FIRST_NAME")
    private String firstName;
    @Column(name = "LAST_NAME")
    private String lastName;
    @Column(name = "GENDER")
    private String gender;
    @Column(name = "DOB")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date dateOfBirth;
    @Column(name = "DOH")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date dateOfHire;
    @ManyToOne(cascade= CascadeType.ALL)
    private Office office;
    @OneToOne(cascade = CascadeType.ALL)
    private ResidenceAddress residence;
    @OneToMany
    private List<Project> projects;

    //getters and setters

}

这是我的EmployeeDao:

public class EmployeeDao implements Serializable{
    private SessionFactory factory;
    private Session session;
    public void addEmployee(Employee employee){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        session.save(employee); 
        session.getTransaction.commit();        
    }
    public List<Employee> getAllEmployee(){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        List<Employee> cities = session.getNamedQuery("Employee.getAllEmployee").list();
        session.close();
        return cities;
    }

    public Employee getEmployeeByEmployeeId(long employeeId){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        Employee employee = (Employee) session.get(Employee.class, employeeId);
        session.close();
        return employee;
    }    
}

问题1) 在这里,我正在关闭会话的方法,然后将结果返回给managedbeans。 所以在员工列表页面中,表列出了名称dob dateOfHire。我有一个buutton视图更详细。单击此按钮,我想显示使用相同管理员工作的所选员工的所有项目,但是它给了我错误(2),无法延迟加载,没有会话或会话已经关闭。 如果我在date的getemployeeMethod中保持会话打开,我想这可能会导致内存泄漏问题或其他问题。是这样吗? 另外,我尝试过懒惰和急切的装载。请给我一个清楚的想法何时/如何使用这些类型的提取。 我怎么解决这个问题?我可以去过滤器或facelisteners来解决这个问题吗?

问题2) 如果我正在尝试编辑员工的项目,并使用session.saveorupadte(),merge(),flush()更新,我得到这样的错误,“具有相同标识符的对象已经与会话关联” 我该如何解决这个问题?

问题3) 我知道sessionfactory是资源消耗。因此,对于一个应用程序,只有一个实例就够会议怎么样? 对于应用程序的单个用户,只需要一个会话? 请告诉我开发这样一个应用程序的好策略。

感谢大家:)

2 个答案:

答案 0 :(得分:0)

我认为您的第一个问题的答案是您不会在每次使用时都提交会话。我的意思是在getAllEmployee和getEmployeeByEmployeeId方法中,您不提交事务。您可以添加 session.getTransaction.commit(); 我不确定,但您的第一个问题可能会导致第二个问题,因为事务未提交。对于会话的好策略,您可以查看this

答案 1 :(得分:0)

正如在另一个答案中所说,你应该commiting你已经开始的交易。在阅读数据时不需要交易。

  

问题1:延迟加载。

您需要lazy load projects,以便可以在视图中自由使用。为此,请修改如下代码,

@OneToMany(fetch=FetchType.LAZY)
private List<Project> projects;
  

问题2:已存在具有相同标识符的对象。

调试并找出要保存的对象的id。如果没有错误的堆栈跟踪,我在这里帮助不大。

  

问题3:session和sessionfactory。

以下是此article的两者之间的区别。

  

SessionFactory是Hibernates单个数据存储区的概念,并且是线程安全的,因此许多线程可以同时访问它并为单个数据库请求编译映射的会话和不可变缓存。 SessionFactory通常只在启动时构建一次。 SessionFactory应该包含在某种单例中,以便可以在应用程序代码中轻松访问它。

     

Session是一个轻量级和非线程安全的对象(不,你不能在线程之间共享),它代表了数据库的单个工作单元。会话由SessionFactory打开,然后在所有工作完成后关闭。 Session是持久性服务的主要接口。会话懒惰地获得数据库连接(即仅在需要时)。为了避免创建太多会话,无论调用currentSession()方法多少次,都可以使用ThreadLocal类来如下所示获取当前会话。

因此,在代码中,您应该为session创建一个局部变量,并在每次关闭方法时将其关闭。