📄 hibernate_note.txt
字号:
共享主键:
主键做外键,方法简明.节省一个字段,利用one-to-one标签.
映射文件:
唯一外键:
po1{
Long oid;
Po2 po2;
}
<class >
<id>产生的机制是随便的</id>
/*<one-to-one>//这里不能用一对一,对这个属性进行一对一映射
这个属性在数据库里面对应的是个外键,而外键对应的关系是多对一.
所以要写<many-to-one>,单向关系只用在一方表达.
*/
<many-to-one
nome="po2"
class="Po2" 这个可以不用写
column=oid2 表示应用的外键
unique=true 表示该字段是唯一的.
cascaded="all"//因为是关联属性所以级联也要说明,就是在数据库里面对PO1的操作要把po2也进行操作.
/>
</class>
共享主键:
po不用改.
主键的产生方式就需要改变
<id name="oid" column="oid">我的oid的值是从我关联的对象来的
<generator class="foreign">id生成策略变成外键
我的主键从外边来,从addr属性来
<param name="property">addr</param>它是从属性address里获得的.
</generator>
</id>
关联字段变成
<one-to-one
name="addr"
constraint="true"//addr属性约束了自己所在类的主键的生成.
表明该类对象的数据库表和被关联对象对应的数据库表之间,通过一个外键引用对主键进行约束
影响save和delete方法,在级联操作时候的先后顺序
cascade="all"
/>
主键的值在这个属性里面.
oid的值是从addr属性,的aid里面来,aid是用hilo计算出来的.计算之后就引用到oid
所以addr的aid即使主键也是外键.
是以属性来描述的,oid的值是来自于aid,constraint表示addr约束了oid的值.
双向的地方对方再加一个one-to-one
一般都会使用共享主键来表示一对一.
集合映射:
mapping collection of value types
值类型:
指的就是非引用类型.基本类型在集合里面变成包装类和字符串都是值类型.
现在存的不是自定义的对象,而是整个基本类型和字符串.
退化的1对多:
一个po和字符串之间的对应关系.
set
元素不重复,无顺序.
集合里面每个元素都是字符串.
映射:
private Set images=new HashSet();
images要映射到其他的表里面,这两个表直接用外键来关联.
集合实际上是存入到另外一张表里面.
<set
name="aliases" 集合
table="t_aliases"对应的表
>
<key column="aid">这个table的外键
<element type="string" column="aliase"> type不可以省略,集合元素的字段,从集合的角度来说,集合里面的元素
</set>
set表示的这张表不是po就没有必要有主键,里面key和表里的id并不是主键,是外键,
需要主键的话可以建立联合主键
drop table t_aliases;
drop table t_person;
create table t_person(
oid integer(11) primary key,
name varchar(11) not null
);
create table t_aliases(
aid integer(11) references t_person(oid),
aliase varchar(50),
constraint person_aliases primary key(aid,aliase)
);
一对多 association
the most important kind of association
双向,一对多:
PO1里面有多个PO2的引用,如果每个PO2里面都有一个PO1的引用,那么就是双向一对多
再java的内存里面,一方有Set引用另一个类型,另一个类型里面有该类型的一个引用.双向一对多.
Company{Set<Employee> employees} 1:n Employee{Company compay;}
尽量设计成双向,业务一但变化,从那边都可以查到对方.
注意多的一方要加上equals和hashcode方法
在映射里面有个关联属性:
关联属性是company,再company里面是employees
po是有表的,set里面全是employee对象,employee对应的有表,employees不对应表
<set
name="employees"
cascade="save-update" 除了删除操作其他操作都进行.
>
<key column="fid"></key>
employee表的外键.e这边多,多的这一方的表起到关联作用的是外键,
一的这一方起关联作用的表是主键
e和c 是一对多的关系,
e里面有c的一个引用,这个引用就是用fid这个外键代表,fid指向c这边的主键.
有集合的这一方是 一,没有集合的这一方是 多
<one-to-many class="Employee"></one-to-many>
</set>
关联属性之所以配置复杂,
employees表达的是一对多,在数据库里面就是主键对外键,
索引这个key是引用我这边主键的外键.
从e关联到c
这个关联属性应用特殊的多对一标签
你要存c就有c来维护这个关系,
你要存c的时候就会先去存e,e呢又有个外间字段,这个字段是空的,我存完e之后,再存c然后才有主键,
那么e才把外键值set进去.
可以用一个属性来解决.invers="true"
改动po给po一个添加成员的方法
public void hire(Employee e){
if(employees!=null){
employees.add(e);
}
e.setCompany(this);
}
public void setCompany(Company company) {
this.company = company;
company.hire(this);
}
这两种写法都是可以的,只是只有一方就够了,
从逻辑上决定权再公司.
再oracle上,eid的值并没有生成,相应的e的hashcode和equale方法都报错.
在<set inverse="true" 存c的话那么碰到inverse就会把主控交给e,e就会现存c在存自己这个时候就有外键了.
>
如果你是关系的主控方你就要先存对方,然后在存自己,这样能最大程度的保证自己的数据完整性,就不会有外键为空的情况.
inverse=true
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into T_Company (name, cid) values (?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
inverse=false
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into T_Company (name, cid) values (?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
Hibernate: insert into T_Employee (name, fid, eid) values (?, ?, ?)
Hibernate: update T_Employee set fid=? where eid=?
Hibernate: update T_Employee set fid=? where eid=?
Hibernate: update T_Employee set fid=? where eid=?
控制权就要交给多的这一方,所以inverse=true就是 (控制权翻转)
在set的关联属性上写inverse那么就是我不管了,把主控交给多个一方.多的一方就会先加载一的一方.
由多的一方维护.
写一个工具类:
直接的返回Session给我们使用。
<set
name="employees",
集合属性表达的就是一对多.
cascade="save-updata"
级联操作.除了删除的所有操作.
inverse="true"
现存关系的另外一方,然后在存自己.
>
<key
column="fid"
one-to-many标签里面,class所描述类型对应的表中的外键.
>
<one-to-many
class="Employee"
Employee表示当前类和它的一对多关系,通过类型描述了它所对应的表
>
</set>
<many-to-one
name="company"
表达多对一的关系,数据库里面用fid表示,表示company对应的表的外键.
column="fid"
这个外键字段的fid
>
多对多 many-to-many
将两个表要表达的多对多,要利用一张中间表,来表达多对多的关系.
Student 1 n
Course m 1
一个学生选多门课程,一门课程可以被多个学生选则.
多对多从逻辑上是个很复杂的情况.
可是你从学生对象里面看不出自己这一方是多还是一,只能知道course是多.
<set name="courses" table="t_enrollment">
<key column="fsid"></key>
<many-to-many //po里面就成了,多对多.
></many-to-many>
</set>
必须要加inverse属性,如果不加会抛出唯一性约束错误.
第四章,
Working with Persistent Objects
持久对象的状态管理.
po的状态,
并不是结构的问题,是po和它托管环境之间的问题.非托管的环境.
po当我们吧一个持久对象new出来之后:
这个对象和数据库没有关系.为什么说它有关系呢,通过.hbm.xml来描述关系,这是逻辑上的关系,实际并没有关系,
因为数据库里面并没有数据,这是一种状态.
当对象被存入数据库之后,数据库确实有了这个数据这又是一种状态.
session又一级缓存,会保存对象的一份副本,对象和副本的变化,这就优势一种状态.
session的save student就和session有了关系和数据库也有了关系,
当我们将session关闭,那么session的缓存就不存在,对象和session就没有关系了,但是数据库里面有记录了,就又是一种状态.
持久对象的三种状态
1,db没有data,和session无关:瞬态(自由态)(暂态),不被session管,在内存中不在数据库中.
2,db中又data,和session有关:持久态,数据库里面有持久数据,而且受session的管理随时和数据库保持同步.
3,db中有data,和session无关:脱管态(游离态),脱离了session的管理.
po有几种状态,有几种特征.
Transient
Persistent
Detached
状态的转化的中间就牵扯道很多语法.
所有的方法都是session的.
new -> Transient
在class标签里面,unsaved-value 默认是等于null,如果希望没有存就等于1
Transient -> Persistent
save() 非常去定对象是个transient的对象,这种对象的初始值有 class标签的unsaved-value指定.
用Long那么默认值就是null,如果用long默认值就是0,那么不能直接和unsaved-value的默认值一致了.当然可以自己指定.
saveOrUpdate() 不确定是什么状态的对象
Persistent -> Transient
delet(o),到数据库里面去吧记录删除,但是对象的oid依然还存在,
这个对象就还可以进行save但是不能进行saveOrUpdate,因为他又oid它就会去做更新,可是数据库里并没有对应的记录了.
hibernate可能认为oid的值和业务有关
已经是持久对象了还要删除它,所以并不经常用了,
Persitent -> Detached
close() session关闭,那么session里面的所有对象都是脱管对象.
clear() session里面有个缓存,保存持久对象的引用,那么一旦调用clear清空了缓存那么session就不认识这些持久对象了,对象变成游离态.
session还在.所有持久对象都全变成游离态
s.save(stu);stu.setName("Tony");如果现在是持久态那么就不用再做更新操作,直接对session做flush或者吧事务commit就可以了.
evict(po) 把po从session的缓存里面删除掉.
这三个函数就可以把持久对象变成
Detached -> Persistent
update(po) 和session再建立联系就可以了.把po和数据库做一个同步.po如果是自由态的那么就报错,对持久态po没有用.
脱管的po回到session的管理下.
saveOrUpdate(po) 和update()用处一样.交给hibernate自己判断是什么状态的对象.
lock(po) 只是和session进行联系,并不跟数据库做同步操作
开始 -> Persistent
这个开始到持久,表示的是session从数据库里面把对象恢复出来到程序里面.
恢复对象的方法:
get(stu.class,oid); 取得指定对象.
load(stu.class,oid); 取得指定对象.
find(); 现在已经不用了.
iterate(); 现在已经不用了.
etc
select 对应的有多种方法.真正的数据的程序,一般都要求你用一个select解决问题.
但是hibernate里面不能用sql,要用oo的方式,要用HQL.这些函数加上HQL读出来的结果都是持久态对象,
garbage jvm
只能回收Transient和detached,
persistent状态的有引用再session里面
整个图把session的使用完全的列出来了.
Session的读取方法:
get(Class,oid),如果找不到所要的数据,不会抛异常,返回一个null.
load(Class,oid),如果找不到所要的数据,就会抛出异常.这个异常可以不用捕捉.
get方法使用立即加载的方式发送sql,
load方法只有对象被访问的时候会发送sql
他们其他的地方都一样,返回要查的对象Object
练习:
cascade="none"忽略级联
save-update关联关系只用来进行插入和更新.
delete 删除
all 所有操作都级联操作.
all-delete-orphan 删除关联的时候把持久对象也一起删除
delete-orphan 从关系种脱离的持久对象也删除.
one-to-one 里面一般是用all
one-to-many save-update
批量更新:Batch update
语法:
for(int i=0;i<100000;i++){
Customer customer=new Customer(....);
session.save(customer);
}tx.commit();session.close();
如果hibernate的批量处理那么,hibernate的缓存里面有个对象大小相似的副本,
一旦进行10万次就会出现,OutOfMemoryException.
那么就只能在中间过程种进行flush();
for(int i=0;i<100000;i++){
Customer customer=new Customer(....);
session.save(customer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -