您的位置:首页 > 其它

Hibernate中的一对一关系

2009-09-18 14:47 169 查看
首先转别人的一片例子:

转贴:http://www.wangchao.net.cn/bbsdetail_68735.html

-----------------------------------------------------------------------------------------------------------------------------------------------

  一对一关联有两种映射方式:一种是使用主键关联,限制两个数据表的主键使用相同的值;另一种是一个外键和一个惟一关键字对应。
  下面的例子采用主键关联。一人一个房间。
  SQL:
  CREATE TABLE user (
   USER_ID varchar(10) NOT NULL default '',
   NAME varchar(16) NOT NULL default '',
   PRIMARY KEY (USER_ID)
  );
  CREATE TABLE room (
   ROOM_ID varchar(10) NOT NULL default '',
   ADDRESS varchar(32) NOT NULL default '',
   PRIMARY KEY (ROOM_ID)
  );
  User.java:
  package ivan.hibernate.one2one;
  public class User {
   private long id;
   private String name;
   private Room room;
   ...//Getters and Setters
  }
  User.hbm.xml:
  <hibernate-mapping>
   <class name="ivan.hibernate.one2one.User" table="USER">
   <id name="id" column="USER_ID" unsaved-value="0">
   <generator class="increment"/>
   </id>
   <property name="name">
   <column name="NAME" length="16" not-null="true"/>
   </property>
   <one-to-one name="room"
   class="ivan.hibernate.one2one.Room"
   cascade="all"/>
   </class>
  </hibernate-mapping>
  Room.java:
  package ivan.hibernate.one2one;
  public class Room {
   private long id;
   private String address;
   private User user;
   ...// Getters and Setters
  }
  Room.hbm.xml:
  <hibernate-mapping>
   <class name="ivan.hibernate.one2one.Room" table="ROOM">
   <id name="id" column="ROOM_ID" unsaved-value="0">
   <generator class="foreign">
   <param name="property">user</param>
   </generator>
   </id>
   <property name="address" type="string"/>
   <one-to-one name="user"
   class="ivan.hibernate.one2one.User"
   constrained="true"/>
   </class>
  </hibernate-mapping>
  hibernate.cfg.xml:
  <hibernate-configuration>
   <session-factory>
   <!-- properties -->
   <property name="connection.username">root</property>
   <property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
   <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
   <property name="connection.password"></property>
   <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
   <mapping resource="ivan/hibernate/one2one/Room.hbm.xml" />
   <mapping resource="ivan/hibernate/one2one/User.hbm.xml" />
   </session-factory>
  </hibernate-configuration>
  Test.java:
  package ivan.hibernate.one2one;
  import net.sf.hibernate.*;
  import net.sf.hibernate.cfg.*;
  public class Test {
   public static void main(String[] args) throws HibernateException {
   SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
  
   Room room = new Room();
   room.setAddress("China-10-911");
  
   User user = new User();
   user.setName("bush");
   user.setRoom(room);
   room.setUser(user);
  
   Session session = sessionFactory.openSession();
   Transaction tx= session.beginTransaction();
   session.save(user);
  
   tx.commit();
   session.close();
   sessionFactory.close();
   }
  }
  运行后,在mysql,hibernate 数据库中可看见 User 表和 Room 表都增加了一条记录。
  存在问题:如果 Room 表已经存在某个 id 值的记录,而增加的 User 记录的 id 将会是此 id 值,将会抛出异常:java.sql.BatchUpdateException: Duplicate entry ...。这是因为 Room 的 id 是根据 User 的 id 来的。如果新增加的 id 在 Room 表中已经存在,就会抛出 SQL 异常。您可以试验,上面步骤成功后,只将表 User 清空。这样表 User 的记录 id 自增又会从 1 开始。当重新运行程序时就会抛出异常。

-----------------------------------------------------------------------------------------------------------------------------------------------



这篇文章是从网上搜索到的,原因是自己的工程当时遇见一个问题,我这边的机器人服务器的Hibernate和另外一边的PHP公用一个数据库的两张表:

uchome_blog:

blogid mediumint(8) unsigned not null

uid mediumint(8) unsigned not null

username char(15) not null

....

uchome_blogfield

blogid mediumint(8) unsigned not null

uid mediumint(8) unsigned not null

message mediumtext not null

.....



现在的情况是:上面两张表关系是一对一,其中,blogfield的主键参照blog的主键,字段名字和值都是相同的,操作中保持级联更新删除。

数据库是MySQL5,配置文件<因为我负责机器人部分去了,我整完框架,这部分就有另外一个同事去做了,配置文件是他写的>:

<class name="UchomeBlog" table="uchome_blog" catalog="stage_uhome">
<id name="blogid" type="java.lang.Integer">
<column name="blogid" />
<generator class="increment" />
</id>
<property name="uid" type="java.lang.Integer">
<column name="uid" not-null="true" />
</property>
<property name="username" type="java.lang.String">
<column name="username" length="15" not-null="true" />
</property>
<property name="subject" type="java.lang.String">
<column name="subject" length="80" not-null="true" />
</property>
<property name="classid" type="java.lang.Short">
<column name="classid" not-null="true" />
</property>
<property name="viewnum" type="java.lang.Integer">
<column name="viewnum" not-null="true" />
</property>
<property name="replynum" type="java.lang.Integer">
<column name="replynum" not-null="true" />
</property>
<property name="tracenum" type="java.lang.Integer">
<column name="tracenum" not-null="true" />
</property>
<property name="dateline" type="long">
<column name="dateline" not-null="true" />
</property>
<property name="pic" type="java.lang.String">
<column name="pic" length="120" not-null="true" />
</property>
<property name="picflag" type="java.lang.Byte">
<column name="picflag" not-null="true" />
</property>
<property name="noreply" type="java.lang.Byte">
<column name="noreply" not-null="true" />
</property>
<property name="friend" type="java.lang.Byte">
<column name="friend" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="10" not-null="true" />
</property>

<one-to-one name="blogfield"
class="UchomeBlogfield"
cascade="all"/>
</class>

<class name="UchomeBlogfield" table="uchome_blogfield" catalog="stage_uhome">

<id name="blogid" column="blogid" unsaved-value="0">
<generator class="foreign">
<param name="property">blog</param>
</generator>
</id>
<property name="uid" type="java.lang.Integer">
<column name="uid" not-null="true" />
</property>
<property name="tag" type="java.lang.String">
<column name="tag" not-null="true" />
</property>
<property name="message" type="java.lang.String">
<column name="message" length="16777215" not-null="true" />
</property>
<property name="postip" type="java.lang.String">
<column name="postip" length="20" not-null="true" />
</property>
<property name="related" type="java.lang.String">
<column name="related" length="65535" not-null="true" />
</property>
<property name="relatedtime" type="long">
<column name="relatedtime" not-null="true" />
</property>
<property name="targetIds" type="java.lang.String">
<column name="target_ids" length="65535" not-null="true" />
</property>

<one-to-one name="blog"
class="UchomeBlog"
constrained="true"/>
</class>



工程能跑起来,开始操作也正常,保存了几次之后,抛出异常:

2009-09-18 10:13:53,312 - 3056875 [pool-1-thread-1] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
2009-09-18 10:13:53,312 - 3056875 [pool-1-thread-1] ERROR org.hibernate.util.JDBCExceptionReporter - Duplicate entry '6659' for key 'PRIMARY'
2009-09-18 10:13:53,328 - 3056891 [pool-1-thread-1] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:575)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:651)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:621)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:311)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:117)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:203)
at $Proxy5.publishBlog(Unknown Source)
at sun.reflect.GeneratedMethodAccessor65.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:203)
at $Proxy6.publishBlog(Unknown Source)
at sun.reflect.GeneratedMethodAccessor65.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:203)
at $Proxy6.publishBlog(Unknown Source)
at com.iouyou.robot.model.helper.MessageProcesser.processMsg(MessageProcesser.java:156)
at com.iouyou.robot.model.QQRobotModel.handleCommonsMessage(QQRobotModel.java:238)
at com.iouyou.robot.model.QQRobotModel.receivedCommonsMessage(QQRobotModel.java:198)
at com.iouyou.robot.model.QQRobotModel.qqEvent(QQRobotModel.java:129)
at edu.tsinghua.lumaqq.qq.QQClient.fireQQEvent(QQClient.java:1084)
at edu.tsinghua.lumaqq.qq.BasicFamilyProcessor.processReceiveIM(BasicFamilyProcessor.java:1836)
at edu.tsinghua.lumaqq.qq.BasicFamilyProcessor.packetArrived(BasicFamilyProcessor.java:275)
at edu.tsinghua.lumaqq.qq.ProcessorRouter.packetArrived(ProcessorRouter.java:64)
at edu.tsinghua.lumaqq.qq.QQClient.firePacketArrivedEvent(QQClient.java:1073)
at edu.tsinghua.lumaqq.qq.PacketEventTrigger.call(PacketEventTrigger.java:47)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:207)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.BatchUpdateException: Duplicate entry '6659' for key 'PRIMARY'
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1669)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1085)
at sun.reflect.GeneratedMethodAccessor64.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.logicalcobwebs.proxool.ProxyStatement.invoke(ProxyStatement.java:100)
at org.logicalcobwebs.proxool.ProxyStatement.intercept(ProxyStatement.java:57)
at $java.sql.Statement$$EnhancerByProxool$$7b04b7fe.executeBatch(<generated>)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
... 52 more



Hibernate 我一般只是当作框架来用,很少深入框架本身里面一些东西,这个错误以前没遇见过。说是主键重复,可是到数据库查那个重复的主键,查不到数据,感觉有点奇怪,而且服务器跑起来之后,是保存了几条记录之后才出现这个问题的。

搜索了一下,于是搜索到上面转贴的帖子,大概理解了一下。于是尝试把映射文件的generator改称为native(在我用Hibernate和MySQL的经历中,从来都是用native的)映射文件如下:



<class name="UchomeBlog" table="uchome_blog" catalog="stage_uhome">
<id name="blogid" type="java.lang.Integer">
<column name="blogid" />
<generator class="native" />
</id>
<property name="uid" type="java.lang.Integer">
<column name="uid" not-null="true" />
</property>
<property name="username" type="java.lang.String">
<column name="username" length="15" not-null="true" />
</property>
<property name="subject" type="java.lang.String">
<column name="subject" length="80" not-null="true" />
</property>
<property name="classid" type="java.lang.Short">
<column name="classid" not-null="true" />
</property>
<property name="viewnum" type="java.lang.Integer">
<column name="viewnum" not-null="true" />
</property>
<property name="replynum" type="java.lang.Integer">
<column name="replynum" not-null="true" />
</property>
<property name="tracenum" type="java.lang.Integer">
<column name="tracenum" not-null="true" />
</property>
<property name="dateline" type="long">
<column name="dateline" not-null="true" />
</property>
<property name="pic" type="java.lang.String">
<column name="pic" length="120" not-null="true" />
</property>
<property name="picflag" type="java.lang.Byte">
<column name="picflag" not-null="true" />
</property>
<property name="noreply" type="java.lang.Byte">
<column name="noreply" not-null="true" />
</property>
<property name="friend" type="java.lang.Byte">
<column name="friend" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="10" not-null="true" />
</property>

<one-to-one name="blogfield"
class="UchomeBlogfield"
cascade="all"/>
</class>

<class name="UchomeBlogfield" table="uchome_blogfield" catalog="stage_uhome">

<id name="blogid" column="blogid" unsaved-value="0">
<generator class="foreign">
<param name="property">blog</param>
</generator>
</id>
<property name="uid" type="java.lang.Integer">
<column name="uid" not-null="true" />
</property>
<property name="tag" type="java.lang.String">
<column name="tag" not-null="true" />
</property>
<property name="message" type="java.lang.String">
<column name="message" length="16777215" not-null="true" />
</property>
<property name="postip" type="java.lang.String">
<column name="postip" length="20" not-null="true" />
</property>
<property name="related" type="java.lang.String">
<column name="related" length="65535" not-null="true" />
</property>
<property name="relatedtime" type="long">
<column name="relatedtime" not-null="true" />
</property>
<property name="targetIds" type="java.lang.String">
<column name="target_ids" length="65535" not-null="true" />
</property>

<one-to-one name="blog"
class="UchomeBlog"
constrained="true"/>
</class>



跑一下,OK。于是不停的测试,不停的发保存记录。跑到了现在几个小时过去,还是没问题。看来问题是在这里了。



但是具体的详细问题出现的原因在哪里,还不知道。现在列出hibernate3中关于主键映射到底是native还是increment的描述:



increment:用于为long, short或者int类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。



native:根据底层数据库的能力选择identity, sequence 或者hilo中的一个。



关于increment的试用,Hibernate API里面说的如此清楚了,呵呵。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: