save肯定是添加的时候用,update修改时候用,saveorupdate是添加或修改,如果真是这样save和update的存在就没意义了,我们直接saveorupdate就行了。
save在添加用的时候 不会出现索引机制(即遍历目录 效率最高)
update在修改时候要遍历 不存在则会异常
saveorupdate是优先遍历 如果不存在则创建(效率最低)
merge和saveOrUpdate方法区别在于:merge方法是把我们提供的对象转变为托管状态的对象;而saveOrUpdate则是把我们提供的对象变成一个持久化对象;
说的通俗一点就是:saveOrUpdate后的对象会纳入session的管理,对象的状态会跟数据库同步,再次查询该对象会直接从session中取,merge后的对
象不会纳入session的管理,再次查询该对象还是会从数据库中取。
Session的save()方法使一个临时对象转变为持久化对象。例如以下代码保存一个Customer对象:
Customer customer = new Customer();
customer.setId(new Long(9)); // 为Customer临时对象设置OID是无效的
customer.setName("Tom");
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(customer);
session.close();
Session的save()方法完成以下的操作:
(1)把Customer对象加入到缓存中,使它变为持久化对象。
(2)选用映射文件指定的标识符生成器为持久化对象分配惟一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定标识符生成器:
<id name="id" column="ID">
<generator class="increment"/>
</id>
以上程序试图通过setId()方法为Customer临时对象设置OID是无效的。假如起初CUSTOMERS表中没有记录,那么执行完save()方法后,Customer对象的ID为1。如果希望由程序来为Customer对象指定OID,可以调用save()的另一个重载方法:
save(customer, new Long(9));
以上save()方法的第二参数显示指定Customer对象的OID。这种形式的save()方法不推荐使用,尤其在使用代理主键的场合,不应该由程序为持久化对象指定OID。
(3)计划执行一个insert语句,把Customer对象当前的属性值组装到insert语句中:
insert into CUSTOMERS(ID, NAME, ......) values(1, 'Tom', ......);
值得注意的是,save()方法并不立即执行SQL insert语句。只有当Session清理缓存时,才会执行SQL insert语句。如果在save()方法之后,又修改了持久化对象的属性,这会使得Session在清理缓存时,额外执行SQL update语句。以下两段代码尽管都能完成相同的功能,但是左边代码仅执行一条SQL insert语句,而右边代码执行一条SQL insert和一条SQL update语句。左边代码减少了操作数据库的次数,具有更好的运行性能。
Customer customer = new Customer(); Customer customer = new Customer();
// 先设置Customer对象的属性,再保存它 session.save(customer);
customer.setName("Tom"); // 先保存Customer对象,再修改它的属性
session.save(customer); customer.setName("Tom");
transaction.commit(); transaction.commit();
Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当Customer对象处于持久化状态时,不允许程序随意修改它的OID,例如:
Customer customer = new Customer();
session.save(customer);
customer.setId(new Long(100)); // 抛出HibernateException
transaction.commit();
以上代码会导致Session在清理缓存时抛出异常。
无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的OID。因此,比较安全的做法是,在定义持久化类时,把它的setId()方法高为private类型,禁止外部程序访问该方法。
Session的save()方法是用来持久化一个临时对象的。在应用程序中不应该把持久化对象或游离对象传给save()方法。例如以下代码两次调用了Session的save()方法,第二次传给save()方法的Customer对角处于持久化状态,这步操作其它是多余的:
Customer customer = new Customer();
session.save(customer);
customer.setName("Tom");
session.save(customer); // 这步操作是多余的
transaction.commit();
在例如以下代码把Customer游离对象传给session2的save()方法,session2会把它当做临时对象处理,再次向数据库中插入一条Customer记录:
Customer customer = new Customer();
customer.setName("Tom");
Session session1 = sessionFactory.openSession();
Transaction tx1 = session1.beginTransaction();
session1.save(customer); // 此时Customer对象的ID变为1
tx1.commit();
session1.close(); // 此时Customer对象变为游离对象
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
session2.save(cutomer); // 此时Customer对象的ID变为2
tx2.commit();
session2.close();
尽管以上程序代码能正常运行,但是会导致CUSTOMERS表中有两条代表相同业务实体的记录,因此不符合业务逻辑。
hibernate提供了saveOrUpdate的方法来进行数据库的操作。hibernate会根据对象的状态决定是insert还是update,
其根本是通过xml文件中unsaved-value来确定的。如果设置null,系统会根据传入的对象的id的值判断,如果是null,
则表示对象不存在,那么insert;如果不是Null,则表示已经存在,那么update.如果设置为none,那么表示对象不存在,
会始终调用insert;如果设置为any,那么表示对象始终存在,会始终调用update
数据库表主键的知识点:
Generator 为每个 POJO 的实例提供唯一标识。一般情况,我们使用“native”。
class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括:
“assigned” 主键由外部程序负责生成,在 save() 之前指定一个。
“hilo” 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。
“seqhilo”与hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,
适用于支持 Sequence 的数据库,如Oracle。
“increment”主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,
之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。
“identity”采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL 中的主键生成机制。
“sequence”采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。
“native”由 Hibernate 根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式。
“uuid.hex”由 Hibernate 基于128 位 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
“uuid.string”与uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中。
“foreign”使用另外一个相关联的对象的标识符作为主键。
以下举例:
1、指定参数的情况:
<id name="id" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_CHILD</param>
</generator>
</id>
使用的是sequence,适合oracle数据库;
2、对于sql server2000中的数据库子增字段,在配置文件使用下列方法实现:
<id name="id" type="long" unsaved-value="0">
<column name="id" sql-type="numeric" not-null="true" />
<generator class="identity" />
</id>
这里主要是:identity:代表由sql server2000数据库自己提供子增字段.如果要hibernate自己提供,则用increment关键字来实现
3、如果表中的主键用字符串类型:可以用hibernate自己提供的方法实现主键唯一:
<id name="id" type="string" unsaved-value="null">
<column name="cid" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
使用的是uuid.hex: 采用128位的算法来生成一个32位字符串。最通用的一种方式。适用于所有数据库。
重要的知识点:
1. 如果有部门表,有员工表,员工表中有dep_id,则表部门类和员工类是one-to-many的关系:
可以使用: ( 在部门类department中使用下列)
Department类:
/** 部门的所有员工 */
private Set staffs = new TreeSet();
xml的文件:
<set name="staffs" >
<key column="dep_id"/>
<one-to-many class="hbp.sys.data.Staff"/>
</set>
如果是list,需要用索引<index> </index>,具体其中的含义,不是很明白.待以后研究
2. 如果部门要有一个负责人,即部门表(tb_department)中有一个字段:staff_id.
那么表示部门和负责人之间的关系是many-to-one的关系
Department类:
/** 部门负责人id */
private Staff staff;
xml 文件
<many-to-one name="staff" class="hbp.sys.data.Staff" column="staff_id"/>
3. 多对多关系,一般我们是做一个中间关联表.我用角色和权限做了个例子,
Right(id,name) Role(id,name) 中间表:tb_role_right(role_id,right_id)
Right类中有一个Role的集合:private Set roles=new TreeSet();
Role类中也有一个Right的集合:private Set rights=new TreeSet();
则两者是明显的多对多关系.使用many-to-many来实现;
xml文件中
right.hbm.xml:如下:
<set name="roles" table="tb_role_right" cascade="all">
<key column="right_id"/>
<many-to-many column="role_id" class="hbp.sys.data.Role"/>
</set>
role.hbm.xml文件中类似:
<set name="rights" table="tb_role_right" cascade="all">
<key column="role_id"/>
<many-to-many column="right_id" class="hbp.sys.data.Right"/>
</set>
4. 几个值得注意的问题:
a)在xml?映射文件中,写类的名字时一定用类的全名:即:包+类名如:(hbp.sys.data.Staff),这个错误使我费了半天劲.:(
b)我在写实现基本DAO操作时,写了
session.delete("from Right as right where right.id="+id);
程序死活报错,我折腾了半天,跟踪到底,才恍然大悟,hibernate在解析sql语句的时候把
其中的right,当成了数据库中的右连接("保留字"),唉,这种关键字,不能随便用啊,:)
5. hibernate中HQL语言的查询根据你的sql的不同而返回不同的对象类型.
如果你使用session.find(String hql)
一般会返回一个List,如:from Staff staff;返回的是包含所有的员工对象的集合
如你的hql为:select count(*) from Staff staff;则返回的是一个Integer对象
如果你使用的hql为:select count(distinct staff.name),count(*) from Staff staff;则返回的是一个Object
即Object[],需要先把他转换成Object[],然后在取[0],[1].
这种设计我不知道hibernate是如何处理的,感觉既好也不好.好的是可以使用一个find获得任意查询
不好在于根据hql来处理返回结果,容易出错.