【问题标题】:How to perform insertion with one to one mapping in hibernate如何在休眠中使用一对一映射执行插入
【发布时间】:2013-09-18 05:41:16
【问题描述】:

我已成功创建两个实体之间的一对一映射。我能够从这两个表中检索数据,但是我无法执行插入操作。

@Entity
@Table(name="users_test")
public class User {
    @Id
    @Column(name="user_id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String username;
    private String password;
    private int enabled;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private UserRole userRole;

       //getters and setters
}

@Entity
@Table(name = "user_roles")
public class UserRole {
    private int user_role_id;
    @Id
    @GeneratedValue(generator = "gen")
    @Column(name = "user_id", unique = true, nullable = false)
    @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="user"))
    private int user_id;
    private String authority;

    @OneToOne(mappedBy="userRole", cascade=CascadeType.ALL)
    private User user;

        //getters and setters
}

我能够通过此映射成功地从表中读取值,但是当我尝试将数据插入表中时出现错误。

public class Runner {
    public static void main(String[] args) {
        Runner run = new Runner();
        ApplicationContext context = new FileSystemXmlApplicationContext("E:\\applicationContext.xml");

        UserDao userDao = (UserDao) context.getBean("userDao");


        UserRole userRole = new UserRole();
        userRole.setAuthority("USER_ROLE");
        userRole.setUser_role_id(0);

        User user = new User();
        user.setUsername("ichigo");
        user.setPassword("kura");
        user.setEnabled(0);
        user.setUserRole(userRole);
        userDao.insertUser(user);
    }

}

用户道:

@SuppressWarnings("unchecked")
@Repository
@Transactional
public class UserDao {
    @Autowired
    SessionFactory sessionFactory;

    public List<User> getUsers() {
        Session currentSession = sessionFactory.getCurrentSession();
        return currentSession.createQuery("from User").list();
    }

    public void insertUser(User user) {
        sessionFactory.getCurrentSession().save(user);
    }
}

我得到以下异常:

Exception in thread "main" org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [test.hibernate.model.UserRole.user]
    at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:101)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:118)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:204)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:189)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:114)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:724)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:716)
    at org.hibernate.engine.spi.CascadingAction$5.cascade(CascadingAction.java:258)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:448)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:293)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:126)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:204)
    at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:189)
    at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:753)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:745)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:741)
    at test.hibernate.dao.UserDao.insertUser(UserDao.java:24)
    at test.hibernate.dao.UserDao$$FastClassByCGLIB$$192f03a.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    at test.hibernate.dao.UserDao$$EnhancerByCGLIB$$7428e072.insertUser(<generated>)
    at test.hibernate.test.main.Runner.main(Runner.java:34)

请指教我做错了什么。


编辑:

从回复看来,我的外键在代码中混杂了。所以只要把问题说清楚,我正在发布表格的架构。

    CREATE TABLE `users_test` (                              
                  `USER_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,    
                  `USERNAME` varchar(45) NOT NULL,                       
                  `PASSWORD` varchar(45) NOT NULL,                       
                  `ENABLED` tinyint(1) NOT NULL,                         
                  PRIMARY KEY (`USER_ID`)                                
                ) ENGINE=InnoDB AUTO_INCREMENT=113 DEFAULT CHARSET=utf8  

CREATE TABLE `user_roles` (                                                                                                   
              `USER_ROLE_ID` int(10) DEFAULT NULL,                                                                                        
              `USER_ID` int(10) unsigned NOT NULL,                                                                                        
              `AUTHORITY` varchar(45) NOT NULL,                                                                                           
              PRIMARY KEY (`USER_ID`),                                                                                                    
              KEY `FK_user_roles` (`USER_ID`),                                                                                            
              CONSTRAINT `FK_user_roles` FOREIGN KEY (`USER_ID`) REFERENCES `users_test` (`USER_ID`) ON DELETE CASCADE ON UPDATE CASCADE  
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8  

【问题讨论】:

  • 为什么要用@Id注释外键user_id?
  • 我从这里构建了上述内容....howtodoinjava.com/2012/11/15/… .... 使用共享主键示例的那个
  • 这段代码中有很多乱七八糟的东西(不在教程中,在问题中)。你的关系的欠站点是什么。似乎是用户。如果是这样,外键也在错误的表中。

标签: java mysql spring hibernate


【解决方案1】:

你的一对一映射也是错误的,外键没有正确使用。在您的示例中,外键使用错误。您的映射表明UserRole 中的primary keyUser 中是foreign key,但事实并非如此。您需要删除@PrimaryKeyJoinColumn 并使用@JoinColumn 映射外键

有两种类型的一对一映射。

单向映射

只有对象的欠侧被映射到关系中。对象没有出现在关系的另一边。

@Entity
@Table(name="users_test")
public class User {
    @Id
    @Column(name="user_id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String username;
    private String password;
    private int enabled;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id")
    private UserRole userRole;

       //getters and setters
}

并且UserRole 类将没有任何User,因为使用了双向映射。

@Entity
@Table(name = "user_roles")
public class UserRole {

    @Id
    @GeneratedValue(generator = "gen")
    @Column(name = "user_role_id", unique = true, nullable = false)
    @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="user"))
    private int user_role_id;
    private String authority;

}

双向映射

两个类都具有这种映射样式的关系。 mappedBy 属性在这里用来强调关系的归属。不需要第二个外键,只需通过适当的映射将User 对象添加到UserRole。在您的情况下,它是由 user 而不是 userRole 映射的。

@Entity
@Table(name = "user_roles")
public class UserRole {

    @Id
    @GeneratedValue(generator = "gen")
    @Column(name = "user_role_id", unique = true, nullable = false)
    @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="user"))
    private int user_role_id;
    private String authority;

    @OneToOne(mappedBy="user", cascade=CascadeType.ALL)
    private User user;

        //getters and setters
}

编辑

还有另一个错误的映射。 UserRole#user_role_id 是用@Id 注释的外键。

【讨论】:

  • 当我使用单向版本时...我能够正确检索表格
  • 但是插入失败并出现 NPE.... 是因为外键问题吗?
  • 如果关系的欠方是用户。我的意思是用户有角色。你的表也错了。映射说 User 是欠款方,而表说 UserRole 是欠款方。
  • 对于单向情况,@Parameter(name="property", value="user") 中的user 引用是什么?
【解决方案2】:

您有一个双向关系,但只设置了关系的一侧。你只是在做user.setUserRole(userRole)。你需要做一个userRole.setUser(user) 到,我喜欢在setUserRole() 的设置器中这样做

public clas User {

    public void setUserRole(UserRole userRole) {
        this.userRole=userRole;
        this.userRole.setUserRole(this);
    }

}

总是这样处理它。 (您可能还需要/想要添加null 检查)。

你的代码中有一个更奇怪的事情,为什么你的UserRole 对象有一个User 和一个user_id 字段?恕我直言,它应该只有一个 User 字段。

@Entity
@Table(name = "user_roles")
public class UserRole {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int user_role_id;

    private String authority;

    @OneToOne(mappedBy="user", cascade=CascadeType.ALL)
    private User user;

    //getters and setters
}

我希望有类似上面的内容。

【讨论】:

  • 不需要设置关系的两边。这取决于关系的欠款方。如果关系的欠方是User,则设置UserRolenedded,或欠方为UserRole,则应设置User
  • 我在过去几年中使用 Hibernate 并没有设置双向关系的经验是,hibernate 经常会感到困惑。因此最好是安全并设置双方,这在OO方面也更好,你应该设置关系的双方。
【解决方案3】:

首先,据我所知,用户和角色实体之间的一对一映射是不正确的。您可能希望您的用户拥有多个角色,对吗?

UserRole 实体不应有对 User 的引用 - UserRole 可以在没有 User 的情况下存在。

一个用户可以有多个角色,一个角色可以分配给多个用户。 (假设这是你打算做的。)

我建议您使用单向多对多或 User 和 UserRole 实体之间的关系。它将为 User-UserRole 映射创建一个单独的表。

例子:-

class User {

     //  ID and other fields


     @ManyToMany(fetch = FetchType.EAGER)
     @JoinColumn(unique = false, nullable = false, name = "ROLE", insertable = true)
     @Fetch(value = FetchMode.JOIN)
     @JoinTable(name="USER_ROLE_MAPPING")
     private List<UserRole> authorities;
}

角色实体:-

class UserRole{

     // id and other fields.

      private String roleName;    // Thats it !!
}

【讨论】:

  • 这里有OneToOne映射。用户有一个 UserRole。此外,可以在 OP 的代码中看到,存在双向映射。我真的不明白你为什么建议ManyToMany。我在问题中看不到任何关于它的信息。
  • 是的,我知道这个问题描述了 OneToOne 映射。我对完全没有这种方式给出了我的“意见”。在大多数用户和角色场景中,情况就是如此。真的不需要投票。总之……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
相关资源
最近更新 更多