用户表:
CREATE TABLE `tbl_user`
(`id` INT(32) NOT NULL AUTO_INCREMENT,`user_name` VARCHAR(20) NULL DEFAULT NULL,
`password` VARCHAR(20) NULL DEFAULT NULL,PRIMARY KEY (`id`)
)
用户会员表:
CREATE TABLE `tbl_member`
(`id` INT(10) NOT NULL AUTO_INCREMENT,`card_no` VARCHAR(50) NULL DEFAULT NULL,
`user_id` INT(11) NULL DEFAULT NULL,PRIMARY KEY (`id`),
INDEX `FK_tbl_member_tbl_user` (`user_id`),
CONSTRAINT `FK_tbl_member_tbl_user` FOREIGN KEY (`user_id`) REFERENCES `tbl_user` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
一对一关联分为主键关联与外键关联。
主键关联:不必加额外的字段,只是主表和辅表的主键相关联,即这两个主键的值是一样的。
外键关联:辅表有一个额外的字段和主表相关联,或者两个表都有额外的字段与对应表的相关联。
public class Users {
private int id;
private String userName;
private String password;
private Account account;
//getter and setter
}
public class Account {
private int id;
private String cardNo;
private Users users;
//getter and setter
}
Main方法:
Session session=null;
Transaction tran=null;
try {
session=HibernateSessionFactory.getSession();
tran=session.beginTransaction();
Users users=new Users();
users.setUserName("zhangsan");
users.setPassword("123456");
Member member=new Member();
member.setCardNo("002568853369_lisi");
member.setUsers(users);
session.save(member);
tran.commit();
} catch (HibernateException e) {
tran.rollback();
e.printStackTrace();
}finally{
session.flush();
session.close();
}
1.单方外键关联
这种关联member是用many-to-one然后用unique="true"做限制,限制成一对一的关系。所以,一对一的外键关联其实就是多对一关联的一种特例。
Member xml
<hibernate-mapping>
<class name="wb.wk.review.mapping.onetoone.bean.Member" table="tbl_member">
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"></generator>
</id>
<property name="cardNo" type="java.lang.String" column="card_no"/>
<many-to-one name="users" column="user_id" unique="true" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
User xml
<hibernate-mapping>
<class name="wb.wk.review.mapping.onetoone.bean.Users" table="tbl_user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"></generator>
</id>
<property name="userName" type="java.lang.String" column="user_name"/>
<property name="password" type="java.lang.String" column="password"/>
<!--
property-ref属性为user,property-ref:表明从Users(users属性)建立了从Users(member属性)对象到Member对象的关联,
只要调用user持久化对象的getMember()方法,就能导航到member对象,
-->
<one-to-one name="member" class="wb.wk.review.mapping.onetoone.bean.Member"
property-ref="users" cascade="all">
</one-to-one>
</class>
</hibernate-mapping>
运行main方法:
出现异常org.hibernate.MappingException: Unknown entity: wb.wk.review.mapping.onetoone.byForeignKey.Member
原因:hibernate.cfg.xml忘记导入相应的xml
再次运行,出现异常:
Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails (`ppi`.`tbl_member`, CONSTRAINT `FK_tbl_member_tbl_user` FOREIGN KEY (`user_id`) REFERENCES `tbl_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
原因是在id属性里必须设置generator 主键生成策略属性,我当时设置了assigned结果出现此异常。
注意:我使用的mysql,所以设置成”identity”,不同的数据库要设置不同的属性类别。具体的参照转载的一篇文章:http://blog.csdn.net/u013803303/article/details/46813249
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"></generator>
</id>
最后运行成功,双表均插入成功。
总结:member表的外键use_id和user表的主键id对应,这没啥好说的。
2.主键关联
user xml
<hibernate-mapping>
<class name="wb.wk.review.mapping.onetoone.bean.Users" table="tbl_user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"></generator>
</id>
<property name="userName" type="java.lang.String" column="user_name"/>
<property name="password" type="java.lang.String" column="password"/>
<one-to-one name="member" class="wb.wk.review.mapping.onetoone.bean.Member" cascade="all" >
</one-to-one>
</class>
</hibernate-mapping>
Member xml:
<hibernate-mapping>
<class name="wb.wk.review.mapping.onetoone.bean.Member" table="tbl_member">
<id name="id" column="id">
<generator class="foreign">
<param name="property">users</param>
</generator>
</id>
<property name="cardNo" type="java.lang.String" column="card_no"/>
<one-to-one name="users" class="wb.wk.review.mapping.onetoone.bean.Users" constrained="true">
</one-to-one>
<!-- constrained属性为true,代表user表的主键id同时作为外键,id必须为foreign生成策略 -->
</class>
</hibernate-mapping>
运行后出现下面异常:
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
.......
Caused by: java.sql.BatchUpdateException: Field 'user_id' doesn't have a default value
发现是数据库没有设置自动增长。设置好运行,又出现:
org.hibernate.PropertyValueException: not-null property references a null or transient value: wb.wk.review.mapping.onetoone.bean.Users.member.
调试后发现是因为外检表(member表)的constrained属性忘记设置了。不设置此属性,member表的外键就为null,出现此异常
再次运行成功。
总结:插入后查询数据库发现,member表的id和user表的id是一一对应的,这应该就能很好的解释了什么是主键关联。但是member表外键user_id为null,由此看来主键关联适用于没有外键关联的,双表关联。
3. 双方外键关联
如果关联双方都是外键关联,那可以两边都用many-to-one。
<many-to-one name="member" cascade="all" unique="true"
column="member_id" />
Account配置(类不变)
<many-to-one name="user" unique="true" column="user_id"
cascade="all" />
这个就不写测试了。
最后网上找了个官方的配置介绍:
<one-to-one name="propertyName" (1) class="ClassName" (2)cascade="cascade_style"(3) constrained="true|false"(4)fetch="join|select" (5)property-ref="propertyNameFromAssociatedClass" (6)access="field|property|ClassName" (7)formula="anySQLexpression" (8)
lazy="proxy|no-proxy|false" (9) entity-name="EntityName"(10)
node="element-name|@attribute-name|element/@attribute|."
mbed-xml="true|false"foreign-key="foreign_key_name"/>
(1) name: 属性的名字。
(2) class (可选 - 默认是通过反射得到的属性类型):被关联的类的名字。
(3) cascade(级联) (可选):表明操作是否从父对象级联到被关联的对象。
(4) constrained(约束) (可选):表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。 这个选项影响save()和delete()在级联执行时的先后顺序以及 决定该关联能否被委托(也在schema export tool中被使用).
(5) fetch (可选 - 默认设置为选择): 在外连接抓取或者序列选择抓取选择其一.
(6) property-ref (可选):指定关联类的属性名,这个属性将会和本类的主键相对应。如果没有指定,会使用对方关联类的主键。
(7) access (可选 - 默认是 property): Hibernate用来访问属性的策略。
(8) formula (可选):绝大多数一对一的关联都指向其实体的主键。在一些少见的情况中, 你可能会指向其他的一个或多个字段,或者是一个表达式,这些情况下,你可以用一个SQL公式来表示。 (可以在org.hibernate.test.onetooneformula找到例子)
(9) lazy (可选 - 默认为 proxy): 默认情况下,单点关联是经过代理的。lazy="no-proxy"指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily)(需要运行时字节码的增强)。 lazy="false"指定此关联总是被预先抓取。注意,如果constrained="false", 不可能使用代理,Hibernate会采取预先抓取!
(10) entity-name (可选): 被关联的类的实体名。