1.4 Hibernate增删改查
接下来以求职者Seeker为例,示范如何使用Hibernate完成单表的增删改查。还记得ORM的概念吗?实体关系映射!要使用ORM,必然要有一个实体类,还有数据表(在执行Hibernate增删改查前,须在Oracle数据库中建立如1.2.3小节所示的表),还需要将二者映射起来(具体体现为一个XML实体类映射文件),最后使用API来完成持久化操作。所以,本节案例的大体思路是:
(1)创建实体类。
(2)创建和配置映射文件。
(3)使用API进行持久化操作。
1.4.1 创建实体类
实体类(也称持久化类)是一个带有一些属性的JavaBean类,实体类对属性的存取方法使用了标准JavaBean命名约定,同时把类属性的访问级别设成私有的。这是推荐的设计,也是面向对象编程的基础。为了通过反射机制来实例化类的对象,需要提供一个无参的构造器,所有的实体类都要求有无参的构造器,因为Hibernate需要使用Java反射机制来创建对象。最后要为实体类实现java.io.Serializable接口,以便Hibernate能更好地缓存实体对象。
创建/5iJob/src/org/ijob/bean/Seeker.java,代码如下:
package org.ijob.bean; import java.SQL.Blob; import java.SQL.Date; import java.util.Set; /** * 求职者基本信息 * */ public class Seeker { private String id; private String email; //邮件 private String password; //密码 private String name; //真实姓名 private String sex="1"; //性别 private Date birth; //出生日期 private int workYear; //工作年限 private String idType; //证件类型 private String idNum; private String residence; //居住地 private double annualSalary; //年薪 private String phoneNum=""; //电话号码 private String homePhoneNum=""; //家庭电话号码 private String compPhoneNum=""; //公司电话号码 private String jobStatus="1"; //工作状态 private String householdRegister; //户籍 private String keyWord; //关键字 private int bodyHigh; //身高 private String currency="1"; //币种 private String maritalStatus="1"; //婚姻状况 private String postcode; //邮编 private String address; //地址 private String qq; //QQ号 private String blog; //个人博客地址 private Blob face; //头像 //此处省略getter&setter方法 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Seeker other = (Seeker) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
1.4.2 创建和配置映射文件
● 创建映射文件
在介绍Hibernate主配置文件hibernate.cfg.xml文件时,提到了该配置文件最后一部分是配置实体映射文件的路径。通过实体映射文件,Hibernate知道怎样去加载和存储实体类的对象,知道应该访问数据库里的哪个表及应该使用表里的哪些字段。
下面是一个实体类映射文件的基本结构:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> ... </hibernate-mapping>
以这个结构为模板,创建/5iJob/src/org/ijob/bean/Seeker.hbm.xml,在其中输入实体类与表的映射信息。
<?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="org.ijob.bean.Seeker" table="ijob_seeker"> <!-- 主键映射 --> <id name="id" type="string"> <column name="id" length="32"></column> <generator class="uuid" /> </id> <!-- 属性映射 --> <property name="email" type="string"> <column name="email" length="100"></column> </property> <property name="password" type="string"> <column name="password" length="20"></column> </property> <property name="name" type="string"> <column name="name" length="10"></column> </property> <!-- 此处省略系列属性映射 --> </class> </hibernate-mapping>
在实体类映射文件中,被映射的类必须定义对应数据库表的主键字段的属性。映射文件的<id>元素定义了该属性到数据库表主键字段的映射。
<id name="propertyName"type="typename"> 1 <column="column_name"></column> 2 <generator class="generatorClass"/> 3 </id>
代码解析:
1.name:属性名,type:Hibernate类型。
2.column:主键字段名,默认为属性名。
3.主键生成器generator是可选的,它用于指定实体对象的标识符(在表中称之为主键)生成策略,下面是一些常见的主键生成策略。
increment:类似于数据表中的自增列,往数据库插入一个新的实体对象时,其标识符(主键)为上一次插入对象的标识符加1。
identity:每次新增对象时,调用数据库本身的identity生成器产生标识符。
sequence:每次新增对象时,调用数据库的sequence对象产生标识符。
配置举例:
<generator class="sequence" > <param name="sequence">sequence_name</param><!--sequence对象的名字 --> </generator>
这样配置之后,每次往数据库中插入新对象,都调用数据库的sequence_name所指定的sequence对象来产生标识符。
uuid:每次新增对象时,用一个128bit的UUID算法生成长度为32的字符串类型的标识符,这是本项目实际采用的主键生成方式。
assigned:不提供主键自动生成支持,而是让应用程序在插入新对象之前手动为对象分配一个标识符,这是没有<generator>元素时的默认生成策略。
● 在主配置文件中添加实体映射文件路径
将映射文件的路径信息添加到hibernate.cfg.xml中,代码如下:
<mapping resource="org/ijob/bean/Seeker.hbm.xml" />
1.4.3 数据库操作
使用Hibernate操作数据库大体上可划分为7个步骤,如图1.6所示。接下来遵循这7个步骤,完成针对单表的增删改查操作。
图1.6 Hibernate操作数据库的7个步骤
● 保存求职者信息
在HibernateTest类中新建方法(方便测试):
public static void save(){ //创建Configuration Configuration cfg = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(cfg.getProperties()).buildServiceRegistry(); //创建SessionFactory SessionFactory factory = cfg.buildSessionFactory(serviceRegistry); //获取Session Session session = factory.openSession(); Transaction tx = session.getTransaction(); try { //开始一个事务 tx.begin(); Seeker seeker = new Seeker(); seeker.setEmail("abc@163.com"); seeker.setPassword("abc"); //保存seeker对象 session.save(seeker); //提交事务 session.getTransaction().commit(); } catch (Exception e) { tx.rollback(); e.printStackTrace(); } finally { //关闭Session session.close(); } }
虽然Seeker类有很多属性,但作为示范,本例只为该类两个属性赋值,其余属性的赋值和这两个属性的赋值类似。特别需要注意,不可以为seeker对象的id属性赋值,因为这里使用uuid主键生成策略,Hibernate框架将自动使用uuid算法为seeker对象生成id属性。Session的save()方法是一个常用方法,用于将实体对象持久化到数据库中。
图1.7显示了数据库中新增一行记录的结果。
图1.7 新增数据结果
复制这个id值(实际得到的id值和本例中的id值不同),在下面的代码中将会用到。
● 根据主键查询求职者信息
具体代码如下:
public static void get(){ Configuration cfg = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(cfg.getProperties()).buildServiceRegistry(); SessionFactory factory = cfg.buildSessionFactory(serviceRegistry); Session session = factory.openSession(); Seeker seeker=(Seeker)session.get(Seeker.class,"替换为id"); System.out.println(seeker.getEmail()+","+seeker.getPassword()); session.close(); }
如代码所示,仅调用Session对象的get()方法便能从数据库中获取一行数据并转换为一个实体对象。Session提供了两种通过主键值加载数据的方法,即get()方法和load()方法。这两个方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。两者区别在于:
(1)如果未能发现符合条件的记录,get()方法返回null,而load()方法会抛出一个ObjectNotFoundException。
(2)load()方法可返回实体的代理类实例,而get()方法直接返回实体类对象。Load()方法可以充分利用Session级缓存(也称一级缓存)和SessionFactory级缓存(也称二级缓存,可以被来自同一个SessionFactory的所有Session共享)中的现有缓存数据,而get()方法则仅仅在Session级缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。因此load()方法可充分地利用二级缓存来提高数据的使用效率,对那些经常读取而较少修改的数据可设置二级缓存,并通过load()方法进行读取,能提高数据加载的效率。load()方法使用如下:
Seeker seeker=(Seeker)session.load(Seeker.class,"替换为id");
(3)get()和load()方法还有一些其他重要区别,将在随后的课程中进行介绍。
● 更新求职者信息
具体代码如下:
public static void update(){ Configuration cfg = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(cfg.getProperties()).buildServiceRegistry(); SessionFactory factory = cfg.buildSessionFactory(serviceRegistry); Session session = factory.openSession(); Seeker seeker=(Seeker)session.get(Seeker.class,"替换为id"); Transaction tx = session.getTransaction(); try { tx.begin(); seeker.setPassword("alex"); //更新seeker对象 session.update(seeker); session.getTransaction().commit(); } catch (Exception e) { tx.rollback(); e.printStackTrace(); } finally { session.close(); } }
如代码所示,修改一个对象的步骤很简单:先加载实体对象,像对象普通操作那样调用setter方法更改其属性值,最后调用Session的update()方法将新的状态持久化到数据库中。
● 删除求职者
具体代码如下:
public static void delete(){ Configuration cfg = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(cfg.getProperties()).buildServiceRegistry(); SessionFactory factory = cfg.buildSessionFactory(serviceRegistry); Session session = factory.openSession(); Seeker seeker=(Seeker)session.get(Seeker.class,"替换为id"); Transaction tx = session.getTransaction(); try { tx.begin(); session.delete(seeker); session.getTransaction().commit(); } catch (Exception e) { tx.rollback(); e.printStackTrace(); } finally { session.close(); } }
如代码所示,删除一个实体对象也很简单:先加载实体对象,然后调用Session的delete()方法就能将该实体对象删除(实际上就是删除了数据表中的一行数据)。
点评
本节示例了如何通过Hibernate API对Seeker类对象执行增删改查操作,从而完成数据库操作。这些操作的前提是配置了数据库连接信息和实体类与数据表的映射关系。进行每一个操作都套用了图1.6所示的7个步骤。这些操作分别使用了Session接口的save()、get()、load()、update()、delete()方法,这些方法都是持久化操作的核心方法,读者有必要熟练掌握。