1.
#hibernate.hbm2ddl.auto create-drop 程序启动,hibernate初始化的时候,删掉表,创建表;程序运行结束后把所有表删掉。
#hibernate.hbm2ddl.auto create 程序启动,在创建表前会把表删掉。当虚拟机退出的时候(在hibernate退出的时候),它不会删表 (和上一种通常在测试环境下使用)
#hibernate.hbm2ddl.auto update 如果不一致,会更新一下表结构
#hibernate.hbm2ddl.auto validate 会校验映射文件跟表结构是否对应,如果不对应,不会update,而是报错。(这种方式更安全一些)
2.
Domain Object限制:
(1).必需要有一个无参构造函数
(2).有无意义的标示符id(主键)(可选)
(3).非final的,对懒加载有影响(可选)
3.
pubic final class HibernateUtil{
private static SessionFactory sessionFactory;
private HibernateUtil(){}
static{
Configuration cfg = new Configuration();
cfg.configure(); //如果文件名不是hibernate.cfg.xml,则可写成cfg.configure("abc.xml");
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
(1).声明为final,因为HibernateUtil类不需要被继承
(2).HibernateUtil不想被new,所以可以把构造方法设为private
(3).虚拟机加载这个类的时候,会执行static里面的代码,而且只执行一次
标准的结构
public class Main {
public static void main(String[] args) {
User user = new User();
user.setBirthday(new Date());
user.setName("name");
addUser(user);
}
static void addUser(User user){
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(user);
tx.commit();
}catch(HibernateException e){
if(tx!=null){
tx.rollback();
throw e;
}finally{
if(s!=null){
s.close();
}
}
}
}
}
4. 未解决的疑问 15分钟左右,关于load的println (解决了?因为session关闭了?)
a.在hibernate.cfg.xml里,<property name="show_sql">true</property>可以在运行时,把sql打印出来
b.session的get和load方法的区别?
最大区别:load不会立刻去访问数据库(所以不会打印出select语句),当第一次使用时,才会去访问数据库。
load返回的是代理,不会立即访问数据库
load是懒加载
在代码 User user1 = (User)session.load(User.class, id);
if(user1 != null){} 中; user1是永远都不会是空的,即使id为-1213212(不存在),原理,在运行load方法时,会new一个User的子类
c.save和persist方法的区别:
save,persist保存数据,persist在事务外不会产生insert语句。
唯一区别: 在没开启事务的时候,save方法会运行insert语句插入数据,但是因为事务没开启,所以回滚了。
persist方法见到没开启事务,就不会插入数据。
Session的几个主要方法
1.save,persist保存数据,persist在事务外不会产生insert语句。
2.delete,删除对象
3.update,更新对象,如果数据库中没有记录,会出现异常。
4.get,根据ID查,会立刻访问数据库。
5.load,根据ID查,(返回的是代理,不会立即访问数据库)。
6.saveOrUpdate,merge(根据ID和version的值来确定是save或update),调用merge你的对象还是托管的。
7.lock(把对象变成持久对象,但不会同步对象的状态)
5.
对象状态
瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
持久(persistent):数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
脱管(detached):数据库中有数据与之对应,但当前没有session与之关联;脱管对象状态发生改变,hibernate不能检测到
eg. session.save(user); user.setName("new name"); tx.commit;
结果,两条语句,一条insert,一条update。 说明处于持久状态的对象,他发生变化的时候,hibernate能知道
eg. session.save(user); user.setName("new name"); user.setBirthday(new Date()); tx.commit;
结果,两条语句,一条insert,一条update。 持久对象变化时不会立即更新数据库,而是等到commit时才做
hbm.xml文件中,<id name="id" unsaved-value=“-1”></id> unsaved-value可以不配,默认情况下,如果id是整数,则unsaved-value是0;如果id是String,则unsaved-value是空。 在保存时,hibernate会先读配置文件,如果配置了这个值,则比较,如果相等,则是瞬时的,就保存
7.
hbm.xml里,
<class name="User" table="`User`">
.....................
</class>
说明:table="`User`",user加了反引号,就不会跟Oracle的关键字冲突
字段名和表的关键字重复,也可以用这个方法解决。如<property name="`name`"/>。(不过,最好还是加column)
etc/hibernate.properties里面,hibernate.cfg.xml里的很多配置属性都可以在这里找到。
hibernate.cfg.xml里,很多属性都有hibernate这个前缀,这个写跟不写都是可以的,hibernate都可以识别到
8.
hql命名参数形式一:
String hql = "from User as user where user.name = ?";
Query query = s.createQuery(hql);
query.setString(0, name);
List<User> list = query.list();
hql命名参数形式二:
String hql = "from User as user where user.name = :n and user.birthday <: birthday";
Query query = s.createQuery(hql);
query.setString("n", name);
query.setDate("birthday", new Date());
List<User> list = query.list();
Query接口的分页查询
query.setFirstResult(0); 设置第一条记录从哪里取
query.setMaxResults(10); 设置取多少条记录
User u = (User)query.uniqueResult();
如果你确定你的结果集最多只有一条,就可以用uniqueResult,但如果返回结果是多条,就会报异常
9.
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("name",name));
c.add(Restrictions.lt("birthday",new Date()));
c.setFirstResult(0);
c.setMaxResults(10);
List<User> list = c.list();
User u = (User)c.uniqueResult();
10.使用Hibernate完成CRUD实验的步骤说明
实验步骤:
1.设计domain对象User.
2.设计UserDao接口.
3.加入hibernate.jar和其依赖的包。
4.编写User.hbm.xml映射文件,可以基于hibernate/eg目录下的org/hibernate/auction/User.hbm.xml修改。
5.编写hibernate.cfg.xml配置文件,可以基于hibernate/etc/hibernate.cfg.xml修改;必须提供的几个参数: connection.driver_class, connect.url, connection.username, connection.password, dialect, hbm2ddl.auto.
6.编写HibernateUtils类,主要用来完成Hibernate初始化和提供一个获得Session的方法:这步可选。
7.实现UserDao接口。
11.
在User.hbm.xml中,
<hibernate-mapping package="cn.itcast.domain">
...................................
</hibernate-mapping>
其中package="cn.itcast.domain",这里package="cn.itcast.domain"是类所在的包,不是说映射文件User.hbm.xml所在的位置。
cn.itcast.domain是相对类来说的,所以用的是点(.),而 在hibernate.cfg.xml中
<mapping resource="cn/itcast/domain/User.hbm.xml"/>, 用的是斜杠(/),就跟磁盘上的文件操作是一样的。
hibernate.cfg.xml和hibernate.properties只需配置一样就可以了,也可以两样都配置。但hibernate.cfg.xml会覆盖hibernate.properties里的内容,就是说xml优先properties
12.
查询的时候,事务打不打开都是可以的。
删除,保存,更新就必须要开启事务了。
在方法 findUserByName(String name)里,
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("name", name));
User user = (User)c.uniqueResult();
uniqueResult()返回是唯一的,这就说如果name不唯一,那就会出错。
这种情况下,如果用户名是唯一的,我们可以做一些限制。 在User.hbm.xml里,
<property name="name"/> 变成<property name="name" unique="true"/>,
这样在创建表的时候,表结构就会加上一个约束,这一列是唯一的。那么用User user = (User)c.uniqueResult();的时候,就不会有问题了。
14.
Department dept = new Department();
dept.setName("dept name");
Employee emp = new Employee();
emp.setDept(dept);
emp.setName("emp name");
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(emp); //这时候,emp属于持久状态,一旦它的属性发生变化,hibernate就察觉到
s.save(dept);
tx.commit();
执行这段代码时,会出现3个sql语句,分别是
Hibernate:insert into Employee(name,dept_id) values(?,?) //这个时候,dept_id是没有值的,是0的
Hibernate:insert into Department(name) values(?) //这时候,dept_id有值了
Hibernate:update Employee set name=?, dept_id=? where id=? //hibernate知道dept_id有值了,知道把emp更新一下
15.
public static void main(String [] args){
Department dept = add();
Employee emp = query(1);
System.out.println("dept name:" + emp.getDept().getName());
}
static Employee query(int empId){
Session s = null;
Transaction tx = null;
try{
s = HibernateUtil.getSession();
tx = s.beginTransaction();
Employee emp = (Employee)s.get(Employee.class, empId);
Hibernate.initialize(emp.getDept());
tx.commit();
return emp;
}finally{
if(s!=null){
s.close();
}
}
}
如果不执行Hibernate.initialize(emp.getDept());这句,则main方法里的println会运行不了,产生懒加载的错误。(org.hibernate.LazyInitializationException)
16.
Employee.hbm.xml里,
<many-to-one name="dept" column="dept_id"/>,
如果不想dept为空,可以加一个约束,变成
<many-to-one name="dept" column="dept_id" not-null="true"/>
17.
a.
Department.java
private Set<Employee> emps;
Department.hbm.xml
<set name="emps">
<key column="dept_id"/>
<one-to-many class="Employee"/>
</set>
b.
告诉Employee,他们是属于那个Depatment的
Department dept = new Department();
dept.setName("dept name");
Employee emp1 = new Employee();
emp1.setDept(dept);
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDept(dept);
emp2.setName("emp name2");
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(dept);
s.save(emp1);
s.save(emp2);
tx.commit();
在这里,因为这两句emp1.setDept(dept); emp2.setDept(dept);
这样就完成了Employee和Department的关联了。
18.一对一主键关联关系的映射与原理分析
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="IdCard" table="id_card">
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="usefulLife" column="useful_life"/>
<one-to-one name="person" constrained="true"/>
</class>
</hibernate-mapping>
19.一对一主键关联关系的检索
Person p = (Person) s.get(Person.class, id);
System.out.println(p.getIdCard().getUsefulLife());
查询主对象Person时,只有一次查询(左外连接 left outer join)就把两个对象全都找出来了,主要是考虑到在一对一的时候,通常查主对象时,都需要从对象
IdCard idCard = (IdCard)s.get(IdCard.class, id);
System.out.println(idCard.getPerson().getName());
查询从对象IdCard时,用了两次查询
<one-to-one name="idCard" property-ref="person"/>
21.
多对多的关系一般不会有update语句出现,因为建立关系的时候就意味着在中间表里插数据,所以不会有这种更新语句的出现
23.
组件关联关系的映射
<component name="name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name" />
</component>
24.
缺省情况下,在一对一的关联中,主对象查从对象是关联查(查一次),其他情况都是查两次
25.
<list name="emps">
<key column="depart_id" />
<list-index column="order_col" />
<one-to-many class="Employee" />
</list>
这个有顺序
<bag name="emps">
<key column="depart_id" />
<one-to-many class="Employee" />
</bag>
这个没顺序, 在java class里面,bag对应List
<map name="emps">
<key column="depart_id" />
<map-key type="string" column="name"/>
<one-to-many class="Employee" />
</map>
这个有key
<array name="employees">
<key column="depart_id" />
<!--表中有单独的整型列表示list-index-->
<list-index column="order_column" />
<one-to-many class="Employee" />
</array>
这个有顺序
集合的简单使用原则:大部分情况下用set,需要保证集合中的顺序用list,想用java.util.List又不需要保证顺序用bag
细节问题:
Set<Employee> emps = new HashSet<Employee>();
emps.add(emp2);
depart.setEmps(emps);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(emp1);
s.save(emp2);
s.save(depart);
tx.commit();
HashSet hs = (HashSet) depart.getEmps();
这句代码HashSet hs = (HashSet) depart.getEmps();在Java代码里是没问题的,但是在Hibernate里却运行不了的。会出现异常: java.lang.ClassCastException:org.hibernate.collection.PersistentSet
出现异常的原因是:hibernate内部做了处理,为了完成懒加载的功能,它把Java的所有集合类都重新写了一遍,就是说虽然保存的是HashSet,但是一保存完,就换成PersistentSet了,PersistentSet实现了Set这个接口,但是没有继承HashSet,它不是HashSet的子类,所以转换肯定出错。
PersistentSet的功能比HashSet要强,这个类可以实现懒加载。
对Hibernate的持久化类来说,里面的定义不能定义成具体的类,必须定义成接口,如:
private Set<Employee> emps;
如定义成:
private HashSet<Employee> emps;
运行时会出现异常。
PersistentSet实现了JDK里面Set这个接口
结论:在持久化类里定义的集合类,要定义成接口,不要定义成具体的类,这样就不会有问题
26.
加入不保存emp1和emp2,代码换成这样
//s.save(emp1);
//s.save(emp2);
s.save(dept);
hbm.xml是这样
<set name="emps">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
运行时会出错,org.hibernate.TransientObjectException:object reference an unsaved transient instance
这是因为dept引用了一个没有保存的瞬时对象emps
相应的sql语句是:
Hibernate:insert into Department(name) values(?) //保存员工
Hibernate:update Employee set dept_id = ? where id = ? //更新相应的员工的外键值,这时候发现员工在数据里根本没有,是瞬时对象,所以报这样的错。
缺省情况下,hiberante不对关联的对象进行操作。 但我们可以改变这个缺省行为,配置级联
<set name="emps" cascade="save-update">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
这时候运行程序就不会有问题了。相应的sql语句是:
hibernate:insert into Department(name) values(?) //保存dept
hibernate:insert into Employee(name,dept_id) values(?,?) //根据配置,对dept干什么事,那么跟dept
hibernate:insert into Employee(name,dept_id) values(?,?) //相关联的employee也要干什么事
hibernate:update Employee set dept_id=? where id=? //维护关系
hibernate:update Employee set dept_id=? where id=?
级联的维护 cascade和inverse(Employee-Department)
Cascade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用cascade:
none, all, save-update, delete, lock, refresh, evict, replicate, persist, merge, delete-orphan(one-to-many下有效,父亲删掉,孤儿也删掉). 一般对many-to-one, many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联
缺省是none,不做任何级联操作。
也可配置多个,如
<set name="emps" cascade="save-update,delete">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
在<one-to-one>中,可以在主对象中加个级联
<class name="Person">
<id name="id">
<generator class="native" />
</id>
<property name="name" />
<one-to-one name="idCard" property-ref="person" cascade="all"/>
</class>
person和idCard是同生共死的关系,就可以把级联设为all。
一般对many-to-one, many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联
27.
Employee emp1 = new Employee();
emp1.setDepart(depart);
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDepart(depart);
emp2.setName("emp name2");
dept.setEmps(emps);
......
s.save(emp1);
s.save(emp2);
s.save(depart);
会产生一下sql语句:
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Department(name) values(?)
Hibernate:update Employee set name=? dept_id=? where id=? //由emp1.setDepart(depart);产生
Hibernate:update Employee set name=? dept_id=? where id=? //由emp2.setDepart(depart);产生
Hibernate:update Employee set dept_id=? where id=? //由dept.setEmps(emps);产生
Hibernate:update Employee set dept_id=? where id=? //由dept.setEmps(emps);产生
有种方式可以让主对象dept放弃对子对象emp的关系的维护,通常会在<one-to-many>的one一方放弃对多的关系的维护,这样效率会高起来(如老师记住每位学生是件困难的事情,效率是很低的,所以干脆就不记了,这关系由学生来维护,学生记住一位老师是很容易)。
<set name="emps" inverse="true">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。 one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。
注:配置成one-to-one的对象不维护关联关系
设置成inverse="true",就算你告诉dept员工是谁,hibernate也不会管(dept.setEmps(emps);这句不起作用)
这样做还有个好处,如代码改成
s.save(depart);
s.save(emp1);
s.save(emp2);
这样代码里就只有3条insert语句,那两条update语句就没了。 从效率上来讲,就好很多了,因为少了update语句。
如果hbm.xml是inverse="false",java代码是
s.save(depart);
s.save(emp1); //因为先保存dept了,所以这时候dept_id已经有值了,所以少了它对应的update语句
s.save(emp2);
那么还是会有两条update语句,是:
Hibernate:insert into Department(name) values(?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:update Employee set dept_id=? where id=? //因为dept属于持久态的,emp1,emp2保存后相当
Hibernate:update Employee set dept_id=? where id=? //于发生了变化,id有值了。持久态的对象,当他的属性发生了变化,那么hibernate会知道这种变化,会把这种变化反应到数据库里面去,反应到数据库的意思就是产生update语句。
如果hbm.xml是inverse="true",java代码是
.......
//emp1.setDept(dept);
//emp2.setDept(dept);
.........
dept.setEmps(emps);
........
s.save(depart);
s.save(emp1); //因为先保存dept了,所以这时候dept_id已经有值了,所以少了它对应的update语句
s.save(emp2);
运行后,会产生3条hibernate的insert语句,查询数据库,select * from employee;会发现 dept_id那一列都没有值。因为虽然告诉dept员工是谁(dept.setEmps(emps);),但是在配置里面inverse="true",告诉dept放弃维护关系了,所以它不会去更新外键的。
所谓关系的维护,就是更新下外键
28.
加入hbm.xml用list会怎样?
<list name="emps" inverse="true">
<key column="depart_id" />
<list-index column="order_col" />
<one-to-many class="Employee" />
</list>
解析:如果定义成list,hibernate会记住员工是第几个加进来的。但用了inverse="true",即是放弃维护关系(就是告诉你员工是谁也不会管了,它就根本不会管顺序了)。
所以使用inverse有限制,不能在有序的集合里使用(比如list,array这些xml元素里面,inverse不能设置为true的)。
inverse只会在集合里面才有。
在Employee.hbm.xml里,
<class name="Employee" discriminator-value="0">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
</class>
<many-to-one>是根本就没有inverse这个属性的,hibernate不允许多的一端放弃对关系的维护,因为多的这一端维护关系从效率来说是比较高的,所以hibernate只允许一的一端放弃维护关系(允许部门放弃对关系的维护,但是不允许员工放弃对关系的维护)
inverse只会在集合里面才有(一对多,多对多),cascade在所有的关联属性都会有(不管集合会有,在一对多,多对多,一对一都会有cascade的配置)。
在hbm.xml中,
<set name="students" table="teacher_student">
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"/>
</set>
在Java中:
t1.setStudents(ss);
t2.setStudents(ss);
s1.setTeachers(ts);
s2.setTeachers(ts);
为什么这四句代码同时有时,程序不能同时运行?
4句代码只能保存其中的2句,inverse缺省为false,缺省是要维护关系的.
一对多只是更新外键,多对多却会在中间表里插记录,这是一对多和多对多的一个区别。
这四句中插数据是会有重复的,所以会出现数据库主键重复的冲突。所以在多对多关联关系中,对象模型你只能告诉一端,不能两端都告诉,如果让两端都维护的话,在对象模型中建立起双向的关联,那么保存的时候就会出现问题。
为什么在一对多的情况下没有这个问题呢? 因为它只是update,它更新两次,值是一样的,它不会报错,但是效率不高。 在多对多就不行了,因为它要在中间表里插记录,插记录有重复的记录就不行了。
如果想让那4句代码同时存在,程序可以运行,方法:让student或者teacher的其中一个放弃对关系的维护。
29.继承关系_整个继承树映射到一张表
<class name="Employee" discriminator-value="0">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type" type="int"/>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<property name="sell"/>
</subclass>
</class>
30.继承关系_每个类映射到一张表
<class name="Employee">
<id name="id">
<generator class="native"/>
</id>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
<joined-subclass name="Skiller" table="skiller">
<key column="emp_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
</class>
#hibernate.hbm2ddl.auto create-drop 程序启动,hibernate初始化的时候,删掉表,创建表;程序运行结束后把所有表删掉。
#hibernate.hbm2ddl.auto create 程序启动,在创建表前会把表删掉。当虚拟机退出的时候(在hibernate退出的时候),它不会删表 (和上一种通常在测试环境下使用)
#hibernate.hbm2ddl.auto update 如果不一致,会更新一下表结构
#hibernate.hbm2ddl.auto validate 会校验映射文件跟表结构是否对应,如果不对应,不会update,而是报错。(这种方式更安全一些)
2.
Domain Object限制:
(1).必需要有一个无参构造函数
(2).有无意义的标示符id(主键)(可选)
(3).非final的,对懒加载有影响(可选)
3.
pubic final class HibernateUtil{
private static SessionFactory sessionFactory;
private HibernateUtil(){}
static{
Configuration cfg = new Configuration();
cfg.configure(); //如果文件名不是hibernate.cfg.xml,则可写成cfg.configure("abc.xml");
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
(1).声明为final,因为HibernateUtil类不需要被继承
(2).HibernateUtil不想被new,所以可以把构造方法设为private
(3).虚拟机加载这个类的时候,会执行static里面的代码,而且只执行一次
标准的结构
public class Main {
public static void main(String[] args) {
User user = new User();
user.setBirthday(new Date());
user.setName("name");
addUser(user);
}
static void addUser(User user){
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(user);
tx.commit();
}catch(HibernateException e){
if(tx!=null){
tx.rollback();
throw e;
}finally{
if(s!=null){
s.close();
}
}
}
}
}
4. 未解决的疑问 15分钟左右,关于load的println (解决了?因为session关闭了?)
a.在hibernate.cfg.xml里,<property name="show_sql">true</property>可以在运行时,把sql打印出来
b.session的get和load方法的区别?
最大区别:load不会立刻去访问数据库(所以不会打印出select语句),当第一次使用时,才会去访问数据库。
load返回的是代理,不会立即访问数据库
load是懒加载
在代码 User user1 = (User)session.load(User.class, id);
if(user1 != null){} 中; user1是永远都不会是空的,即使id为-1213212(不存在),原理,在运行load方法时,会new一个User的子类
c.save和persist方法的区别:
save,persist保存数据,persist在事务外不会产生insert语句。
唯一区别: 在没开启事务的时候,save方法会运行insert语句插入数据,但是因为事务没开启,所以回滚了。
persist方法见到没开启事务,就不会插入数据。
Session的几个主要方法
1.save,persist保存数据,persist在事务外不会产生insert语句。
2.delete,删除对象
3.update,更新对象,如果数据库中没有记录,会出现异常。
4.get,根据ID查,会立刻访问数据库。
5.load,根据ID查,(返回的是代理,不会立即访问数据库)。
6.saveOrUpdate,merge(根据ID和version的值来确定是save或update),调用merge你的对象还是托管的。
7.lock(把对象变成持久对象,但不会同步对象的状态)
5.
对象状态
瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
持久(persistent):数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
脱管(detached):数据库中有数据与之对应,但当前没有session与之关联;脱管对象状态发生改变,hibernate不能检测到
eg. session.save(user); user.setName("new name"); tx.commit;
结果,两条语句,一条insert,一条update。 说明处于持久状态的对象,他发生变化的时候,hibernate能知道
eg. session.save(user); user.setName("new name"); user.setBirthday(new Date()); tx.commit;
结果,两条语句,一条insert,一条update。 持久对象变化时不会立即更新数据库,而是等到commit时才做
hbm.xml文件中,<id name="id" unsaved-value=“-1”></id> unsaved-value可以不配,默认情况下,如果id是整数,则unsaved-value是0;如果id是String,则unsaved-value是空。 在保存时,hibernate会先读配置文件,如果配置了这个值,则比较,如果相等,则是瞬时的,就保存
7.
hbm.xml里,
<class name="User" table="`User`">
.....................
</class>
说明:table="`User`",user加了反引号,就不会跟Oracle的关键字冲突
字段名和表的关键字重复,也可以用这个方法解决。如<property name="`name`"/>。(不过,最好还是加column)
etc/hibernate.properties里面,hibernate.cfg.xml里的很多配置属性都可以在这里找到。
hibernate.cfg.xml里,很多属性都有hibernate这个前缀,这个写跟不写都是可以的,hibernate都可以识别到
8.
hql命名参数形式一:
String hql = "from User as user where user.name = ?";
Query query = s.createQuery(hql);
query.setString(0, name);
List<User> list = query.list();
hql命名参数形式二:
String hql = "from User as user where user.name = :n and user.birthday <: birthday";
Query query = s.createQuery(hql);
query.setString("n", name);
query.setDate("birthday", new Date());
List<User> list = query.list();
Query接口的分页查询
query.setFirstResult(0); 设置第一条记录从哪里取
query.setMaxResults(10); 设置取多少条记录
User u = (User)query.uniqueResult();
如果你确定你的结果集最多只有一条,就可以用uniqueResult,但如果返回结果是多条,就会报异常
9.
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("name",name));
c.add(Restrictions.lt("birthday",new Date()));
c.setFirstResult(0);
c.setMaxResults(10);
List<User> list = c.list();
User u = (User)c.uniqueResult();
10.使用Hibernate完成CRUD实验的步骤说明
实验步骤:
1.设计domain对象User.
2.设计UserDao接口.
3.加入hibernate.jar和其依赖的包。
4.编写User.hbm.xml映射文件,可以基于hibernate/eg目录下的org/hibernate/auction/User.hbm.xml修改。
5.编写hibernate.cfg.xml配置文件,可以基于hibernate/etc/hibernate.cfg.xml修改;必须提供的几个参数: connection.driver_class, connect.url, connection.username, connection.password, dialect, hbm2ddl.auto.
6.编写HibernateUtils类,主要用来完成Hibernate初始化和提供一个获得Session的方法:这步可选。
7.实现UserDao接口。
11.
在User.hbm.xml中,
<hibernate-mapping package="cn.itcast.domain">
...................................
</hibernate-mapping>
其中package="cn.itcast.domain",这里package="cn.itcast.domain"是类所在的包,不是说映射文件User.hbm.xml所在的位置。
cn.itcast.domain是相对类来说的,所以用的是点(.),而 在hibernate.cfg.xml中
<mapping resource="cn/itcast/domain/User.hbm.xml"/>, 用的是斜杠(/),就跟磁盘上的文件操作是一样的。
hibernate.cfg.xml和hibernate.properties只需配置一样就可以了,也可以两样都配置。但hibernate.cfg.xml会覆盖hibernate.properties里的内容,就是说xml优先properties
12.
查询的时候,事务打不打开都是可以的。
删除,保存,更新就必须要开启事务了。
在方法 findUserByName(String name)里,
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("name", name));
User user = (User)c.uniqueResult();
uniqueResult()返回是唯一的,这就说如果name不唯一,那就会出错。
这种情况下,如果用户名是唯一的,我们可以做一些限制。 在User.hbm.xml里,
<property name="name"/> 变成<property name="name" unique="true"/>,
这样在创建表的时候,表结构就会加上一个约束,这一列是唯一的。那么用User user = (User)c.uniqueResult();的时候,就不会有问题了。
14.
Department dept = new Department();
dept.setName("dept name");
Employee emp = new Employee();
emp.setDept(dept);
emp.setName("emp name");
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(emp); //这时候,emp属于持久状态,一旦它的属性发生变化,hibernate就察觉到
s.save(dept);
tx.commit();
执行这段代码时,会出现3个sql语句,分别是
Hibernate:insert into Employee(name,dept_id) values(?,?) //这个时候,dept_id是没有值的,是0的
Hibernate:insert into Department(name) values(?) //这时候,dept_id有值了
Hibernate:update Employee set name=?, dept_id=? where id=? //hibernate知道dept_id有值了,知道把emp更新一下
15.
public static void main(String [] args){
Department dept = add();
Employee emp = query(1);
System.out.println("dept name:" + emp.getDept().getName());
}
static Employee query(int empId){
Session s = null;
Transaction tx = null;
try{
s = HibernateUtil.getSession();
tx = s.beginTransaction();
Employee emp = (Employee)s.get(Employee.class, empId);
Hibernate.initialize(emp.getDept());
tx.commit();
return emp;
}finally{
if(s!=null){
s.close();
}
}
}
如果不执行Hibernate.initialize(emp.getDept());这句,则main方法里的println会运行不了,产生懒加载的错误。(org.hibernate.LazyInitializationException)
16.
Employee.hbm.xml里,
<many-to-one name="dept" column="dept_id"/>,
如果不想dept为空,可以加一个约束,变成
<many-to-one name="dept" column="dept_id" not-null="true"/>
17.
a.
Department.java
private Set<Employee> emps;
Department.hbm.xml
<set name="emps">
<key column="dept_id"/>
<one-to-many class="Employee"/>
</set>
b.
告诉Employee,他们是属于那个Depatment的
Department dept = new Department();
dept.setName("dept name");
Employee emp1 = new Employee();
emp1.setDept(dept);
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDept(dept);
emp2.setName("emp name2");
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(dept);
s.save(emp1);
s.save(emp2);
tx.commit();
在这里,因为这两句emp1.setDept(dept); emp2.setDept(dept);
这样就完成了Employee和Department的关联了。
18.一对一主键关联关系的映射与原理分析
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="IdCard" table="id_card">
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="usefulLife" column="useful_life"/>
<one-to-one name="person" constrained="true"/>
</class>
</hibernate-mapping>
19.一对一主键关联关系的检索
Person p = (Person) s.get(Person.class, id);
System.out.println(p.getIdCard().getUsefulLife());
查询主对象Person时,只有一次查询(左外连接 left outer join)就把两个对象全都找出来了,主要是考虑到在一对一的时候,通常查主对象时,都需要从对象
IdCard idCard = (IdCard)s.get(IdCard.class, id);
System.out.println(idCard.getPerson().getName());
查询从对象IdCard时,用了两次查询
<one-to-one name="idCard" property-ref="person"/>
21.
多对多的关系一般不会有update语句出现,因为建立关系的时候就意味着在中间表里插数据,所以不会有这种更新语句的出现
23.
组件关联关系的映射
<component name="name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name" />
</component>
24.
缺省情况下,在一对一的关联中,主对象查从对象是关联查(查一次),其他情况都是查两次
25.
<list name="emps">
<key column="depart_id" />
<list-index column="order_col" />
<one-to-many class="Employee" />
</list>
这个有顺序
<bag name="emps">
<key column="depart_id" />
<one-to-many class="Employee" />
</bag>
这个没顺序, 在java class里面,bag对应List
<map name="emps">
<key column="depart_id" />
<map-key type="string" column="name"/>
<one-to-many class="Employee" />
</map>
这个有key
<array name="employees">
<key column="depart_id" />
<!--表中有单独的整型列表示list-index-->
<list-index column="order_column" />
<one-to-many class="Employee" />
</array>
这个有顺序
集合的简单使用原则:大部分情况下用set,需要保证集合中的顺序用list,想用java.util.List又不需要保证顺序用bag
细节问题:
Set<Employee> emps = new HashSet<Employee>();
emps.add(emp2);
depart.setEmps(emps);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(emp1);
s.save(emp2);
s.save(depart);
tx.commit();
HashSet hs = (HashSet) depart.getEmps();
这句代码HashSet hs = (HashSet) depart.getEmps();在Java代码里是没问题的,但是在Hibernate里却运行不了的。会出现异常: java.lang.ClassCastException:org.hibernate.collection.PersistentSet
出现异常的原因是:hibernate内部做了处理,为了完成懒加载的功能,它把Java的所有集合类都重新写了一遍,就是说虽然保存的是HashSet,但是一保存完,就换成PersistentSet了,PersistentSet实现了Set这个接口,但是没有继承HashSet,它不是HashSet的子类,所以转换肯定出错。
PersistentSet的功能比HashSet要强,这个类可以实现懒加载。
对Hibernate的持久化类来说,里面的定义不能定义成具体的类,必须定义成接口,如:
private Set<Employee> emps;
如定义成:
private HashSet<Employee> emps;
运行时会出现异常。
PersistentSet实现了JDK里面Set这个接口
结论:在持久化类里定义的集合类,要定义成接口,不要定义成具体的类,这样就不会有问题
26.
加入不保存emp1和emp2,代码换成这样
//s.save(emp1);
//s.save(emp2);
s.save(dept);
hbm.xml是这样
<set name="emps">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
运行时会出错,org.hibernate.TransientObjectException:object reference an unsaved transient instance
这是因为dept引用了一个没有保存的瞬时对象emps
相应的sql语句是:
Hibernate:insert into Department(name) values(?) //保存员工
Hibernate:update Employee set dept_id = ? where id = ? //更新相应的员工的外键值,这时候发现员工在数据里根本没有,是瞬时对象,所以报这样的错。
缺省情况下,hiberante不对关联的对象进行操作。 但我们可以改变这个缺省行为,配置级联
<set name="emps" cascade="save-update">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
这时候运行程序就不会有问题了。相应的sql语句是:
hibernate:insert into Department(name) values(?) //保存dept
hibernate:insert into Employee(name,dept_id) values(?,?) //根据配置,对dept干什么事,那么跟dept
hibernate:insert into Employee(name,dept_id) values(?,?) //相关联的employee也要干什么事
hibernate:update Employee set dept_id=? where id=? //维护关系
hibernate:update Employee set dept_id=? where id=?
级联的维护 cascade和inverse(Employee-Department)
Cascade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用cascade:
none, all, save-update, delete, lock, refresh, evict, replicate, persist, merge, delete-orphan(one-to-many下有效,父亲删掉,孤儿也删掉). 一般对many-to-one, many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联
缺省是none,不做任何级联操作。
也可配置多个,如
<set name="emps" cascade="save-update,delete">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
在<one-to-one>中,可以在主对象中加个级联
<class name="Person">
<id name="id">
<generator class="native" />
</id>
<property name="name" />
<one-to-one name="idCard" property-ref="person" cascade="all"/>
</class>
person和idCard是同生共死的关系,就可以把级联设为all。
一般对many-to-one, many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联
27.
Employee emp1 = new Employee();
emp1.setDepart(depart);
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDepart(depart);
emp2.setName("emp name2");
dept.setEmps(emps);
......
s.save(emp1);
s.save(emp2);
s.save(depart);
会产生一下sql语句:
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Department(name) values(?)
Hibernate:update Employee set name=? dept_id=? where id=? //由emp1.setDepart(depart);产生
Hibernate:update Employee set name=? dept_id=? where id=? //由emp2.setDepart(depart);产生
Hibernate:update Employee set dept_id=? where id=? //由dept.setEmps(emps);产生
Hibernate:update Employee set dept_id=? where id=? //由dept.setEmps(emps);产生
有种方式可以让主对象dept放弃对子对象emp的关系的维护,通常会在<one-to-many>的one一方放弃对多的关系的维护,这样效率会高起来(如老师记住每位学生是件困难的事情,效率是很低的,所以干脆就不记了,这关系由学生来维护,学生记住一位老师是很容易)。
<set name="emps" inverse="true">
<key column="depart_id"/>
<one-to-many class="Employee" />
</set>
inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。 one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。
注:配置成one-to-one的对象不维护关联关系
设置成inverse="true",就算你告诉dept员工是谁,hibernate也不会管(dept.setEmps(emps);这句不起作用)
这样做还有个好处,如代码改成
s.save(depart);
s.save(emp1);
s.save(emp2);
这样代码里就只有3条insert语句,那两条update语句就没了。 从效率上来讲,就好很多了,因为少了update语句。
如果hbm.xml是inverse="false",java代码是
s.save(depart);
s.save(emp1); //因为先保存dept了,所以这时候dept_id已经有值了,所以少了它对应的update语句
s.save(emp2);
那么还是会有两条update语句,是:
Hibernate:insert into Department(name) values(?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:insert into Employee(name, dept_id) values(?,?)
Hibernate:update Employee set dept_id=? where id=? //因为dept属于持久态的,emp1,emp2保存后相当
Hibernate:update Employee set dept_id=? where id=? //于发生了变化,id有值了。持久态的对象,当他的属性发生了变化,那么hibernate会知道这种变化,会把这种变化反应到数据库里面去,反应到数据库的意思就是产生update语句。
如果hbm.xml是inverse="true",java代码是
.......
//emp1.setDept(dept);
//emp2.setDept(dept);
.........
dept.setEmps(emps);
........
s.save(depart);
s.save(emp1); //因为先保存dept了,所以这时候dept_id已经有值了,所以少了它对应的update语句
s.save(emp2);
运行后,会产生3条hibernate的insert语句,查询数据库,select * from employee;会发现 dept_id那一列都没有值。因为虽然告诉dept员工是谁(dept.setEmps(emps);),但是在配置里面inverse="true",告诉dept放弃维护关系了,所以它不会去更新外键的。
所谓关系的维护,就是更新下外键
28.
加入hbm.xml用list会怎样?
<list name="emps" inverse="true">
<key column="depart_id" />
<list-index column="order_col" />
<one-to-many class="Employee" />
</list>
解析:如果定义成list,hibernate会记住员工是第几个加进来的。但用了inverse="true",即是放弃维护关系(就是告诉你员工是谁也不会管了,它就根本不会管顺序了)。
所以使用inverse有限制,不能在有序的集合里使用(比如list,array这些xml元素里面,inverse不能设置为true的)。
inverse只会在集合里面才有。
在Employee.hbm.xml里,
<class name="Employee" discriminator-value="0">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
</class>
<many-to-one>是根本就没有inverse这个属性的,hibernate不允许多的一端放弃对关系的维护,因为多的这一端维护关系从效率来说是比较高的,所以hibernate只允许一的一端放弃维护关系(允许部门放弃对关系的维护,但是不允许员工放弃对关系的维护)
inverse只会在集合里面才有(一对多,多对多),cascade在所有的关联属性都会有(不管集合会有,在一对多,多对多,一对一都会有cascade的配置)。
在hbm.xml中,
<set name="students" table="teacher_student">
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"/>
</set>
在Java中:
t1.setStudents(ss);
t2.setStudents(ss);
s1.setTeachers(ts);
s2.setTeachers(ts);
为什么这四句代码同时有时,程序不能同时运行?
4句代码只能保存其中的2句,inverse缺省为false,缺省是要维护关系的.
一对多只是更新外键,多对多却会在中间表里插记录,这是一对多和多对多的一个区别。
这四句中插数据是会有重复的,所以会出现数据库主键重复的冲突。所以在多对多关联关系中,对象模型你只能告诉一端,不能两端都告诉,如果让两端都维护的话,在对象模型中建立起双向的关联,那么保存的时候就会出现问题。
为什么在一对多的情况下没有这个问题呢? 因为它只是update,它更新两次,值是一样的,它不会报错,但是效率不高。 在多对多就不行了,因为它要在中间表里插记录,插记录有重复的记录就不行了。
如果想让那4句代码同时存在,程序可以运行,方法:让student或者teacher的其中一个放弃对关系的维护。
29.继承关系_整个继承树映射到一张表
<class name="Employee" discriminator-value="0">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type" type="int"/>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<property name="sell"/>
</subclass>
</class>
30.继承关系_每个类映射到一张表
<class name="Employee">
<id name="id">
<generator class="native"/>
</id>
<property name="name" unique="true"/>
<many-to-one name="depart" column="depart_id"/>
<joined-subclass name="Skiller" table="skiller">
<key column="emp_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
</class>
相关推荐
标题 "CS4207_WinVista_Win7_32-64-bit_6-6001-1-30" 暗示这是一款针对Windows Vista和Windows 7操作系统,同时支持32位和64位系统的驱动程序更新。其中,"CS4207"可能是设备的型号或者系列,而"6-6001-1-30"可能是...
### 易语言1-30课教程知识点梳理 #### 一、教程概述 《易语言1-30课教程》是一套由觅风老师精心录制的视频教程,旨在帮助编程初学者快速掌握易语言的基本用法及进阶技巧。该教程共包含30节课,每节课时长大约20...
亮亮图文1-30
大学英语词汇星火式巧记速记精炼1-6Word-List-1-30.doc
Vue常见面试题1-30
Smart Match- 投资以色列报告(英文)-1-30页.pdf
世联行-哈尔滨12月房地产市场月报-1-30页.pdf
资源名称:python零基础1-30讲整合包教程内容: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
新概念 第三册 笔记 1-30.doc。出国前的参考资料
创维高清机顶盒手动升级包(2015-1-30)
上交所-科创板培训PPT-2019.1-30页.pdf
2022中国适老化电视调研报告-中国家电网-2022.1-30页.pdf
1-30一套详细的轨道机器人自动巡检施工方案.docx
1-30一套详细的弱电系统施工图设计说明书.doc
中国健康管理市场数字化升级2018-2019.1-30页.rar
2019猪肉市场变化带来的冷链机遇与挑战-中物联-2020.1-30页
《康熙字典》1-30画的汉字.doc
执业医师模拟考第二套1-30题