标签使用和detached entity passed to persist: com.xiehande.jpa.domain.Clas

在学习的过程中,有一个这样的情况。

添加学生时,需要在下拉框里面显示出班级然后提供选择。当然,实体类里面设置好了映射关系了。也就是student实体类中已经有了clas的属性值并提供getter/setter

在jsp页面上

<s:form action="AddStudentAction" method="post">
                <s:textfield label="学号"  name="student.std_Id" maxlength="14"></s:textfield><br/>
                <s:textfield label="姓名" name="student.std_Name" maxlength="50"></s:textfield>
                <s:select label="性别" name="student.sex" list="#{'1':'男','0':'女'}"></s:select>
                <sx:datetimepicker label="生日" name="student.birthday" displayFormat="yyyy-MM-dd"></sx:datetimepicker>
                <s:select label="选择班级" list="#request.clases" name="c_id" listKey="c_id" listValue="class_Name" headerKey="0" headerValue="班级"></s:select>
                <s:submit value="确定添加"></s:submit>
                <s:reset value="取消操作"></s:reset>
            </s:form>

其他的没什么问题,这里主要是select标签

正确做法:

 <s:select label="选择班级" list="#request.clases" name="c_id" listKey="c_id" listValue="class_Name" headerKey="0" headerValue="班级"></s:select>

摸索了一段时候后, 如果写成:

<s:select label="选择班级" list="#request.clases" name="student.clas.c_id" listKey="c_id" listValue="class_Name" headerKey="0" headerValue="班级"></s:select>

JPA或者hibernate是无法帮你做好student和clas之间的关系的。原因是jsp文件执行之后通过了放射机制映射到AddStudentAction中的student,但是,这个时候,student这个对象里面是没有clas这个对象了。所以,在select标签里面设置name="student.clas.c_id"是无法成功添加clas的。

那么,怎么办呢?

我们可以取到select中传过来的c_id,然后在AddStudentAction中根据这个c_id从数据库读取出这个clas。

  @Override
    public String execute() throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        String c_idstr=request.getParameter("c_id");
        int c_id=0;
        if(!c_idstr.trim().equals("")&&c_idstr!=null){
            c_id=Integer.parseInt(c_idstr);
        //    System.out.println("能不能获得select里面的c_id呢?"+c_id);
        }
        Clas clas=clasDao.getClas(c_id);
      //  System.out.println("你几班的?"+clas.getClass_Name());
        if(student!=null){
            student.setClas(clas);
          boolean flag=studentDao.addStudent(student);
          System.out.println("返回值"+flag);
        }
        return SUCCESS;
    }

对!你发现可以获取到clas了。按理说

          student.setClas(clas);
          boolean flag=studentDao.addStudent(student);

即可保存到数据库。

但是,却报javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.xiehande.jpa.domain.Clas

原因是,clas对象本身在数据库中有,当我student.setClas(clas)的时候,还没有实例化到数据库就有了ID值,而且这个ID值就是以前存进去的id.这是不正常,因为ID没有设置过,而且JPA默认的行为是只要指定了主键生成策略,主键就不能设置了,一旦不为空或者0就被认为是已经保存到了数据库中,一旦调用persist()方法就会抛出上面的异常。

解决的办法有:

1.更改级联配置:
把cascade = CascadeType.ALL 改成 cascade=CascadeType.REFRESH 。(级联让他刷新就好,id值无冲突,自然可以操作) 但,个人不建议这样做,因为你还是想级联过程多 几种,ALL常被使用。

2.对于Action来说必须采用prototype(每次调用创建一个对象)的作用域,修改方法是:在Action上就一个注解@Scope("prototype")

3.将保存student的方法em.persist(student)改成方法em.merge(student);

public boolean addStudent(Student student) {
        boolean flag = false;
        if (student != null) {
            em = JPAEntityManagerFactory.getEntityManager();
            try {
                em.getTransaction().begin();
                em.merge(student);
                em.getTransaction().commit();
                flag = true;
            } catch (Exception ex) {
                ex.printStackTrace();
                em.getTransaction().rollback();
                flag = false;
            } finally {
                em.close();
            }


        }
        return flag;
    }


这个情况实际上开起来不难,但是,如果不是很有经验的程序员的话,也是挺难发现这种问题。

你可能感兴趣的:(标签使用和detached entity passed to persist: com.xiehande.jpa.domain.Clas)