本文包括以下四个部分:
一、增删改查方法
需求:将客户对象保存进数据库。
User.java
package edu.scut.b_curd; public class User { private int id; private String name; private String password; private String email; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", password=" + password + ", email=" + email + "]"; } }User表User.hbm.xml
<pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="edu.scut.b_curd.User" table="t_user"> <!-- 主键 --> <id name="id" column="t_id"> <generator class="assigned"></generator> </id> <!-- 普通属性 --> <property name="name" column="t_name" type="string" length="20"></property> <property name="password" column="t_password" length="20"></property> <property name="email" column="t_email" length="50"></property> </class> </hibernate-mapping>
1.1 保存
在事务当中,如果发生异常,就需要回滚,将数据还原为之前的状态。
public void test1(){ //1 获取session对象 Session session = HibernateUtil.getSession(); //2 开启事务 session.beginTransaction(); try { //3 保存user User user = new User(); user.setId(2); user.setName("张三丰"); user.setPassword("666666"); user.setEmail("[email protected]"); session.save(user); //4 提交事务 session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); //出现异常,回滚事务 session.getTransaction().rollback(); } finally{ //关闭资源 HibernateUtil.close(session); } }
1.2 更新
有两种方式,第一种方式需要对对象的每一项数据都要修改,否则将成为null;第二种只操作需要修改的属性,更为常用。
public void test2(){ //1 获取session对象 Session session = HibernateUtil.getSession(); //2 打开事务 session.beginTransaction(); try { //3 更新操作,有两种方式 /* * 第一种方式: * <1>必须给id赋值; * <2>hibernate会把所有字段都更新,如果对象的属性不赋值,则设置null */ /*User user = new User(); user.setId(1); user.setName("荆天明"); user.setPassword("666666"); user.setEmail("[email protected]"); session.update(user); */ /* * 第二种方式(推荐的方式) * <1>先把需要更新的对象查询出来 * <2>参数一:查询的对象 * <3>参数二:ID值 */ User user = (User) session.get(User.class,1); //修改需要更新的属性 user.setName("盖聂"); user.setPassword("88888888"); //4 提交事务 session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); //有异常,回滚事务 session.getTransaction().rollback(); } finally{ //5 关闭资源 HibernateUtil.close(session); } }
1.3 查询
较常用的是以下三种方式。
public void test3(){ //1 获取session对象 Session session = HibernateUtil.getSession(); //2 打开事务 session.beginTransaction(); try { //3 查询用户 //<1>查询所有用户,hql Query query = session.createQuery("from User"); List<User> users = query.list(); for (User user : users) { System.out.println(user); } //<2>查询一个用户 User user = (User) session.get(User.class,1); System.out.println(user); //<3>条件查询一个用户 Query q = session.createQuery("select u from User u where name=? "); q.setParameter(0, "盖聂"); User u = (User) q.uniqueResult(); System.out.println(u); //4 提交事务 session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); //有异常,回滚事务 session.getTransaction().rollback(); } finally{ //5 关闭资源 HibernateUtil.close(session); } }
1.4 删除
两种删除方式,推荐使用第一种。
public void test4(){ //1 获取session对象 Session session = HibernateUtil.getSession(); //2 打开事务 session.beginTransaction(); try { //3 删除用户 //第一种,先查询出来 User user = (User) session.get(User.class, 1); if(user!=null){ session.delete(user); } //第二种(不推荐) User user2 = new User(); user2.setId(2); session.delete(user2); //4 提交事务 session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); //有异常,回滚事务 session.getTransaction().rollback(); } finally{ //5 关闭资源 HibernateUtil.close(session); } }
二、集合映射
需求:
客户购物时填写收货地址,收货地址是基本类型,而且有多个,这时应采用集合对地址进行封装。
1.1 Set集合
客户类属性:
private int id; private String name; //使用Set集合来存储收货地址:元素无序的 private Set<String> addressSet = new HashSet<String>();User.hbm.xml
<!-- 映射set集合 name:Set集合的属性名称 table:存储收货地址数据的表 key:配置用户的外键 --> <set name="addressSet" table="t_address"> <!-- 外键 --> <key column="u_id"></key> <!-- Set元素的信息 type:数据类型 column:存放Set的数据字段名称 --> <element column="address_name" type="string"></element> </set>
1.2 List集合
客户类属性:
private int id; private String name; //使用List集合来存储收货地址:元素是有序的 private List<String> addressList = new ArrayList<String>();User.hbm.xml
<!-- 映射List集合 --> <list name="addressList" table="t_address_list"> <!-- 外键 --> <key column="u_id"></key> <!-- list的索引字段(排序) --> <list-index column="idx"></list-index> <!-- list元素 --> <element column="address_name" type="string" ></element> </list>
1.3 Map集合
客户类属性
<pre name="code" class="html"> private int id; private String name;
User.hbm.xml
<!-- 映射Map集合 --> <map name="addressMap" table="t_address_map"> <!-- 外键 --> <key column="u_id"></key> <!-- key元素 --> <map-key type="string" column="address_no"></map-key> <!-- value元素 --> <element type="string" column="address_name"></element> </map>
三、关联关系映射
本文只对常用的集中映射关系做讲解。
一对多(单向)、多对一(单向)、一对多(双向)这三类映射采用客户(Customers)和订单(Orders)举例子,其中Customers是一方,Orders是多方。
3.1 多对一(单向)
a. 关系映射图解
注意:多对一(单向)要在多方的属性当中加入一方,以此来建立单向关联。
本例中在多方实体类中添加:private Customers customers;
b. Customers.hbm.xml
<hibernate-mapping> <class name="edu.scut.a_many2one_single.Customers" table="t_customers"> <id name="id" column="c_id"> <generator class="increment"></generator> </id> <property name="name" column="c_name"></property> </class> </hibernate-mapping>
c. Orders.hbm.xml
<hibernate-mapping> <class name="edu.scut.a_many2one_single.Orders" table="t_orders"> <id name="id" column="o_id"> <generator class="increment"></generator> </id> <property name="orderno" column="o_orderno"></property> <!-- 配置多对一的关系 cascade:级联操作 save-uodate:保存或者修改订单的同时,修改客户 delete:删除订单的同时删除客户 all:save+update+delete的功能之和 none:默认,没有级联操作 --> <many-to-one name="customers" column="c_id" class="edu.scut.a_many2one_single.Customers" cascade="all"></many-to-one> </class> </hibernate-mapping>
建立了关系,并且配置了cascade,就可以实现级联操作。
public void test1(){ //打开session Session session = HibernateUtil.getSession(); //打开事务 s ession.beginTransaction(); //添加用户 Customers customers = new Customers(); customers.setName("东方朔"); //添加订单 Orders o1 = new Orders(); o1.setOrderno("5166001"); Orders o2 = new Orders(); o2.setOrderno("5166002"); //建立关系(orders>>customers) o1.setCustomers(customers); o2.setCustomers(customers); //保存数据 //如果采用了级联操作,直接保存订单,客户也可以保存 //如果不配置相应的级联操作,那么会出现异常:(object references an unsaved transient instance - save the transient ) session.save(o1); session.save(o2); //提交事务 session.getTransaction().commit(); //关闭资源 HibernateUtil.close(session); }
3.2 一对多(单向)
a. 关系映射图解
注意:一对多(单向)要在一方的属性当中加入集合来封装多方数据,以此来建立单向关联。
本例在一方实体类中添加:private Set<Orders> orders = new HashSet<Orders>();
b. Customers.hbm.xml
<hibernate-mapping> <class name="edu.scut.b_one2many_single.Customers" table="t_customers"> <id name="id" column="c_id"> <generator class="increment"></generator> </id> <property name="name" column="c_name"></property> <!-- 配置一对多的关系 name:属性名称 --> <set name="orders" cascade="all"> <!-- key:外键字段名称 --> <key column="c_id"></key> <!-- set集合的元素类型 --> <one-to-many class="edu.scut.b_one2many_single.Orders"/> </set> </class> </hibernate-mapping>
c. Orders.hbm.xml
<hibernate-mapping> <class name="edu.scut.b_one2many_single.Orders" table="t_orders"> <id name="id" column="o_id"> <generator class="increment"></generator> </id> <property name="orderno" column="o_orderno"></property> </class> </hibernate-mapping>
d. 测试类(以save()方法为例)
建立了关系,并且配置了cascade,就可以实现级联操作。
public void test1(){ //打开session Session session = HibernateUtil.getSession(); //打开事务 session.beginTransaction(); //添加用户 Customers customers = new Customers(); customers.setName("项羽"); //添加订单 Orders o1 = new Orders(); o1.setOrderno("4326001"); Orders o2 = new Orders(); o2.setOrderno("5436002"); //建立关系(customers>>orders) customers.getOrders().add(o1); customers.getOrders().add(o2); //保存数据 session.save(customers); //提交事务 session.getTransaction().commit(); //关闭资源 HibernateUtil.close(session); }
3.3 一对多/多对一(双向)
将一对多(单向)和多对一(单向)同时配置,并建立双向关系就是一会多(双向)。
注意:一对多(双向)要在一方的属性当中加入集合来封装多方数据,并在多方的属性当中加入一方的对象,以此来建立双向关联。
本例在一方实体类中添加:private Set<Orders> orders = new HashSet<Orders>(); 在多方实体类中添加: private Customers customers;
a. Customers.hbm.xml
<hibernate-mapping> <class name="edu.scut.c_one2many_double.Customers" table="t_customers"> <id name="id" column="c_id"> <generator class="increment"></generator> </id> <property name="name" column="c_name"></property> <!-- 配置一对多的关系 name:属性名称 --> <p align="left"> <!-- inverse=true,如果出现在单方的话,表示由多方维护关系,默认是false,表示双方共同维护关系</p><p align="left"> 如果双方共同维护关系的话,就会出现如下二种情况:</p><p align="left"> 1)SQL多余了,但不出错 -》一对多双向关联</p><p align="left"> 2)SQL多余了,但出错 -》多对多双向关联</p><p align="left"> --></p><p align="left"><span style="color:red;"> </span> <set name="orders" cascade="all" inverse="true"> </p> <!-- key:外键字段名称 --> <key column="c_id"></key> <!-- set集合的元素类型 --> <one-to-many class="edu.scut.c_one2many_double.Orders"/> </set> </class> </hibernate-mapping>
b. Orders.hbm.xml
<hibernate-mapping> <class name="edu.scut.c_one2many_double.Orders" table="t_orders"> <id name="id" column="o_id"> <generator class="increment"></generator> </id> <property name="orderno" column="o_orderno"></property> <!-- 配置多对一的关系 --> <many-to-one name="customers" column="c_id" class="edu.scut.c_one2many_double.Customers" cascade="all" /> </class> </hibernate-mapping>
d. 测试类(以save()方法为例)
建立了双向关系,并且配置了cascade,就可以实现级联操作。
public void test1(){ //打开session Session session = HibernateUtil.getSession(); //打开事务 session.beginTransaction(); //添加用户 Customers customers = new Customers(); customers.setName("项羽"); //添加订单 Orders o1 = new Orders(); o1.setOrderno("4326001"); Orders o2 = new Orders(); o2.setOrderno("5436002"); //建立双向关系 //客户>>订单 customers.getOrders().add(o1); customers.getOrders().add(o2); //订单>>客户 o1.setCustomers(customers); o2.setCustomers(customers); //保存数据,保存客户和订单的任意一方,都可级联保存另一方 session.save(customers); //提交事务 session.getTransaction().commit(); //关闭资源 HibernateUtil.close(session); }
问题:有了一对多关联之后,保存数据时,会多余两条update语句!
解决方案:在一对多的关联关系配置中,在一方 把inverse="true"即可!
3.4 多对多(双向)
多对多(双向)采用老师(Teacher)和学生(Student)举例子。
注意:多对多(双向)要在双方的实体类中同时加入集合封装对方的数据,以此来建立双向关联。多对多(双向)的关联中存在一个中间表,依赖中间表来建立两个表之间的关系。
本例在Student实体类中添加:private Set<Teachers> teachers = new HashSet<Teachers>();
在Teacher实体类中添加: private Set<Students> students = new HashSet<Students>();
a. 关系映射图解
b. Students.hbm.xml
<hibernate-mapping> <class name="edu.scut.d_many2many_double.Students" table="t_student"> <id name="id" column="s_id"> <generator class="increment"></generator> </id> <property name="name" column="s_name"></property> <!-- 配置多对多的关系 table:中间表的名称 --> <set name="teachers" table="student_teacher" inverse="true"> <!-- 当前对象所在中间表的外键 --> <key column="s_id"></key> <!-- set集合的元素类型 --> <!-- column:集合类型所在中间表的外键 --> <many-to-many class="edu.scut.d_many2many_double.Teachers" column="t_id"></many-to-many> </set> </class> </hibernate-mapping>
c. Teachers.hbm.xml
<hibernate-mapping> <class name="edu.scut.d_many2many_double.Teachers" table="t_teacher"> <id name="id" column="t_id"> <generator class="increment"></generator> </id> <property name="name" column="t_name"></property> <!-- 配置多对一的关系 --> <set name="students" table="student_teacher" cascade="all"> <!-- 当前对象所在中间表的外键 --> <key column="t_id"></key> <!-- set集合类型 --> <!-- column:集合类型所在中间表的外键 --> <many-to-many class="edu.scut.d_many2many_double.Students" column="s_id"></many-to-many> </set> </class> </hibernate-mapping>
d. 测试类(以save()方法为例)
建立了双向关系,并且配置了cascade,就可以实现级联操作。
public void test1(){ //打开session Session session = HibernateUtil.getSession(); //打开事务 session.beginTransaction(); //保存学生 Students s1 = new Students(); s1.setName("小强"); Students s2 = new Students(); s2.setName("小花"); //保存老师 Teachers t1 = new Teachers(); t1.setName("李老师"); Teachers t2 = new Teachers(); t2.setName("卢老师"); //建立双向关系 //学生>>老师 s1.getTeachers().add(t1); s1.getTeachers().add(t2); s2.getTeachers().add(t1); s2.getTeachers().add(t2); //老师>>学生 t1.getStudents().add(s1); t1.getStudents().add(s2); t2.getStudents().add(s1); t2.getStudents().add(s2); //保存数据,保存客户和订单的任意一方,都可级联保存另一方 session.save(t1); session.save(t2); //提交事务 session.getTransaction().commit(); //关闭资源 HibernateUtil.close(session); }
问题:在多对多双向关联中,SQL多余了,而且报错。
解决方案:在任的多方把inverse="true"即可!
3.5 一对一(双向)
一对一(双向)采用公民和身份证举例子。
注意:一对一(双向)要在双方的实体类中同时加入对方的对象,以此来建立双向关联。
本例在Personst实体类中添加:private IdCards idcards;
在IdCards实体类中添加: private Persons persons;
a. 关系映射图解
b. IdCards.hnm.xml
有两种方式建立一对一(双向)的关系。
<hibernate-mapping> <class name="edu.scut.e_one2one_double.IdCards" table="t_idcards"> <id name="id" column="i_id"> <generator class="increment"></generator> </id> <property name="cardno" column="i_cardno"></property> <!-- 一对一配置方式有两种 --> <!-- 第一种:唯一外键 是一种特殊的多选一方式,但是外键多了unique约束 --> <!-- <many-to-one name="persons" class="edu.scut.e_one2one_double.Persons" column="p_id" unique="true"> </many-to-one> --> <!-- 第二种:主键关联 constrained:是否把主键作为外键 false:不作为外键 true:作为外键 --> <one-to-one name="persons" class="edu.scut.e_one2one_double.Persons" constrained="true"> </one-to-one> </class> </hibernate-mapping>
c. Persons.hbm.xml
<hibernate-mapping> <class name="edu.scut.e_one2one_double.Persons" table="t_persons"> <id name="id" column="p_id"> <generator class="increment"></generator> </id> <property name="name" column="p_name"></property> <!-- 配置一对一的关系 --> <one-to-one name="idcards" class="edu.scut.e_one2one_double.IdCards" cascade="all"> </one-to-one> </class> </hibernate-mapping>
d. 测试类(以save()方法为例)
建立了双向关系,并且配置了cascade,就可以实现级联操作。
public void test1(){ //打开session Session session = HibernateUtil.getSession(); //打开事务 session.beginTransaction(); //公民 Persons p = new Persons(); p.setName("杨过"); //身份证 IdCards ic = new IdCards(); ic.setCardno("6104271990"); //建立关系 //公民>>身份证 p.setIdcards(ic); //身份证>>公民 ic.setPersons(p); //保存数据 session.save(p); //提交事务 session.getTransaction().commit(); //关闭资源 HibernateUtil.close(session); }
Inverse属性是在维护关联关系的时候起作用的,表示控制权是否转移。
Inverse= false 不反转; 当前方有控制权。
Inverse= True 控制反转; 当前方没有控制权。
默认是 Inverse= false,在一对多(双向)和多对多(双向)的关联关系中,如果不明确配置,则默认Inverse= false,即两边都具有控制权,在执行保存操作的时候,双方能否会维护表。导致一对多(双向)的关联关系出现出现多余的SQL语句,而多对多(双向)关联中直接报错(org.hibernate.exception.ConstraintViolationException:could not execute statement, Caused by:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Duplicate entry '7-7' for key 'PRIMARY'),提示中间表有重复的主键。因此,需要自己手动配置Inverse=“true”。
4.2 cascade属性
cascade 表示级联操作(可以设置到一的一方或多的一方)