马士兵java教程第九部分Hibernate.docx
《马士兵java教程第九部分Hibernate.docx》由会员分享,可在线阅读,更多相关《马士兵java教程第九部分Hibernate.docx(29页珍藏版)》请在冰豆网上搜索。
马士兵java教程第九部分Hibernate
本笔记以annotation为主,忽略xml的配置
第一课
1、需要的jar包(不包含annotationjar)
Annotation需要的jar包
Ps:
最后一个是反射的时候用的
第十三课
日志slf4jlog4j
Slf4j-api定义的是一个个的接口,而不是具体的实现。
具体的实现有多种
常用的是实现是log4j
使用log4j那就必须引用相应的jar包。
Log4j-1.2.17.jar
Slf4j-log4j12-1.7.2.jar
配置文件:
第十四课
1、建立一个专门测试的源文件sourcefolder
2、善于利用@BeforeClass@AfterClass
Pl:
publicstaticSessionFactorysf=null;
@BeforeClass
publicstaticvoidbeforeClass(){
sf=newAnnotationConfiguration().configure("hibernate/hibernate.cfg.xml").buildSessionFactory();
}
@AfterClass
publicstaticvoidcloseSf(){
sf.close();
}
/*static{
sf=newAnnotationConfiguration().configure("hibernate/hibernate.cfg.xml").buildSessionFactory();
}*/
第二十课
ID生成策略
使用xml形式
uuid要求主键是string类型,生成一个128位的独一无二的字符串
native相当于主键加一个autoincrement。
要想数据库实现跨平台,用native或者uuid
使用annotation形式
在getId()方法前加@GenerateValue默认为auto,相当于xml的native。
@id
@GenerateValue
PublicintgetId(){
Returnid;
}
使用sequence方法:
1.@GeneratedVale(strategy=GenerationType.SQUENCE,generator=”teacherSQE”)在getId()方法前注解
2.@SequenceGenerator(name=”teacherSQE”,sequenceName=”teacherSQE_DB”)在类前注解(name=”teacherSQE”指的是SquenceGenerator的名字))
具体过程如下:
在生成id的时候,知道id生成策略为Strategy=GenerationType.SQUENCE,(即是sequence)然后sequence生成策略是teacherSQE在找有没teacherSQE生成策略发现有,生成的sequence的名字是teacherSQE_DB
一般方法@GeneratedValue(strategy=generationType.AUTO)
用一张表生成主键
@java.persistence.TableGenerator{
Name=”Teacher_GEN”,
TABLE=”表名”,
pkColumnName=”pk_key”,
valueColumnName=”pk_value”,
pkColumnValue=”teacher”,
alllcationSize=1;//每一次过后加1用的
}用的非常少,但是这能解决跨数据库平台的问题。
相当于selectvaluefromGENERATOR_TABLEwherekey=teacher结果就是1然后自动加1;
第二十四课
联合主键
原来这样的
联合主键:
联合主键首先要把组合主键的字段独立的建立在一个类中。
并且这个类要实现Serializable.并且主键类要重写equals(),hashCode()方法;
使用annotation的id生成策略
首先需要把主键类实现序列化java.io.Serializable接口
并且重写equals方法和hashCode方法。
因为虽然这样做主键能保证在数据库里面的唯一性,但是并不能保证给对象到内存中仍然唯一。
所以要重写这连个方法。
1.以TeacherPK为例首先在这个表前加上@Embeddable(可以被嵌入的)再在Teacher类中得getPK()方法前加上@id(即在组件的属性上);
2.在getPK()方法前加上@EmbeddedId(在组件的属性上注解);常用
3.在getid()和getName()方法上都加上@id,在teacher类上加上@IdClass(value=TeacherPK.class)常用
第二十六课
核心开发接口
openSession()和getCurrentSession()的区别
1、前者每次都是新的,需要close,后者是从上下文中找,有、用旧地;没有、用新的。
2、前者可以单独使用,后者必须在transaction中使用。
使用后者的前提是hibernate.cfg.xml中配置"current_session_context_class”(thread:
当前线程里面找;jta:
)属性(用途:
1、界定事物边界,2、事物提交,自动close)
第二十八课
对象的三种状态
1三种状态的区分关键在于
a)有没有ID
b)ID在数据库中有没有
c)在内存中有没有(session缓存)
2三种状态:
a)transient:
内存中一个对象,没ID,缓存中也没有
b)persistent:
内存中有,缓存中有,数据库有(ID)
c)detached:
内存有,缓存没有,数据库有ID
ps:
缓存中存在指有个引用指向该对象
第二十九课
delete把一个persist状态的对象删除掉。
Ps:
不管使用的是getCurrentSession(),还是openSession(),如果不使用transaction是无法删除对象的
能删除
@Test
publicvoiddeleteTest(){
Sessionsess=sf.openSession();
Teachert=null;
sess.beginTransaction();
t=(Teacher)sess.get(Teacher.class,41);
System.out.println(t.getId());
sess.delete(t);
sess.getTransaction().commit();
}
不能删除
@Test
publicvoiddeleteTest(){
Sessionsess=sf.openSession();
Teachert=null;
t=(Teacher)sess.get(Teacher.class,41);
System.out.println(t.getId());
sess.delete(t);
}
第三十、三十一课
Session
管理一个数据库的任务单元(session帮助管理数据库的增删改查。
)
get和load都能从数据库里面取出数据;
session.get(Teacher.class,1)从Teacher表里取出id=1的记录(完整的对象)
session.load(Teacher.class,1)从Teacher表里取出id=1的记录(代理对象)
ps:
get和load的区别:
load返回的是个代理对象,等到真正用到的时候才执行sql语句,这时候读取的该对象的id;get直接从数据库里面加载,不会延迟。
@Test
publicvoidloadTest(){
Sessionsess=sf.getCurrentSession();
Teachert=null;
sess.beginTransaction();
t=(Teacher)sess.load(Teacher.class,42);
sess.getTransaction().commit();
System.out.println(t.getId());//t.getName()会报错
}
这样不会报错,因为该代理对象保存有id。
如果是别的属性就会错。
第三十二、三课
update方法
flush()方法:
强制把缓存中的数据与数据库里面的数据做同步。
第三十五课
关系映射(重要)
1一对一单向外键关联
a)Annotation:
在被约束表字段的get方法上加@0neTo0ne@JoinColumn
@OneToOne
@JoinColumn(name="wifeid")//指定生成的数据库字段名
publicWifegetWife(){
returnwife;
}
packagecom.wang.model;
importjavax.persistence.*;
@Entity
@Table(name="t_husband")
publicclassHusband{
privateintid;
privateStringname;
privateWifewife;
@Id
@GeneratedValue
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
@OneToOne
@JoinColumn(name="wifeId")
publicWifegetWife(){
returnwife;
}
publicvoidsetWife(Wifewife){
this.wife=wife;
}
}
b)xml:
在被约束表的xml配置文件中加
unique="true"是保证生成的字段唯一,这样2一对一双向外键关联
a)项目名称:
hibernate_0700_one2one_bi_fk^
b)Annotation:
@0ne20ne(mappedBy=”另一个类里定义的属性名”)
规律:
凡是双向关联,必设mappedBy
在Wife类中写Husband对象属性并添加注解@OneToOne(mappedBy="wife")mappedBy作用
是指定这个一对一关联是被Husband类的wife属性(准确说是getWife方法)做的映射
@OneToOne(mappedBy="wife")
publicHusbandgetHusband(){
returnhusband;
}
在类中写Wife对象属性
@OneToOne
@JoinColumn(name="wifeid")//指定生成的数据库字段名
publicWifegetWife(){
returnwife;
}
此注释将由Husband表中生成wifeid字段作为fk外键,wife表中不生成额外的Husbandid字段
packagecom.wang.model;
importjavax.persistence.*;
@Entity
@Table(name="t_wife")
publicclassWife{
privateintid;
privateStringname;
privateHusbandhusband;
@OneToOne(mappedBy="wife")
//@JoinColumn(name="husbandId")设置双向的这个就不用了
publicHusbandgetHusband(){
returnhusband;
}
publicvoidsetHusband(Husbandhusband){
this.husband=husband;
}
@Id
@GeneratedValue
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
}
c)xml:
many-to-oneunique在Student类中写StuIdCard属性,StuIdCard类中写Student属性
StuIdCard.hbm.xml文件中加
Student.hbm.xml文件中加
其中,property-ref相当于mappedBy
此方式生成的StuIdCard表中包含studentid字段作为fk外键,Student表中不生成额外的字段
特别说明:
一对一单向外键关联与一对一双向外键关联在数据库的表的格式是一样的,区别在于
java程序中.双向外键关联可通过Hibernate在两个类间互相调用彼此,而单向外键关联只能单方向调用.
组合主键的映射(比较少用)
联合主键用@joinColumns()
Ps:
在Husband类上的getWife()上注解,引用的字段当然是wife类中的“id,name“。
第四十一课
组建映射
把一种表整体的嵌入到另一种表中,这样的映射行为即为组建映射。
第四十二课
多对一单向关联
在user类中的
privateGroupgroup
@ManyToOne
@JoinCloumn(name=“groupId”)
publicGroupgetGroup(){}
在User.cfg.xml中
一对多单向
在group类中
privateSetset=newHashSet();
@OneToMany
@JoinColumn(name=”groupId”)(ps:
没有这一句,将会当做特殊的多对多关系处理,会建立一个中间表)
Xml形式
第四十四课
一对多多对一双向
在user类中的
privateGroupgroup
@ManyToOne
@JoinCloumn(name=“groupId”)
publicGroupgetGroup(){…}
在group类中
@OneToMany(mappedBy=”group”)
publicSetgetSet(){…}
xml形式就是把一对多和多对一的xml都写上
第四十五课
多对多单向(少用)
多对多的实现要在两张表中间建立一张中间表
默认情况生成的中间表
@ManyToMany
@JoinTable(name=”t_s”,
joinColumns={@joinColumn(name=”teacher_id”)},
inverseJoinColumns={@joinColumn(name=”student_id”)}
)
PublicSetgetStudents(){…}
Ps:
joinTable()指定生成的中间表的相关信息。
Xml
table定义中间表的表名
(中间表对应当前表的字段)
Teacher
Student
第四十五课
多对多双向关联(更少用)
直接在另一边加上、
@ManyToMany(mappedBy="")
第四十七——五十三课
关联关系的CRUDcascadefetch
Cascade
User类和Group类双向关联
@Test
publicvoidtest1(){
Sessionsess=sf.openSession();
sess.beginTransaction();
Useru=newUser();
u.setName("wang");
Groupg=newGroup();
g.setName("gwang");
u.setGroup(g);
sess.save(u);
sess.getTransaction().commit();
}
Ps:
这个能级联存储g
@Test
publicvoidtest2(){
Sessionsess=sf.getCurrentSession();
sess.beginTransaction();
Useru=newUser();
u.setName("u2");
Groupg=newGroup();
g.setName("g2");
g.getUsers().add(u);
sess.save(g);
sess.getTransaction().commit();
}
Ps:
这个却不能级联存储u
只用在group类那边也设这cascade={CasecadeType.ALL}才能级联操作。
但是,这样操作的级联却是这样的
groupId里的值为NULL;
在加上u.setGroup(g);这样就没有问题了。
Ps:
站在hibernate的角度,存储User的时候,必须知道那个groupId是多少,而该groupId就存在Group类中。
铁律:
如果表之间的关联是双向的,那么在程序中也要设置成双向的。
即既要有g.getUsers().add(u);也要有u.setGroup(g);不然可能会出现问题。
fetch
ps:
一对多默认是fetct=FetchType.lazy
多对一默认的fetch=FetchType.eager
第五十九—六十三课
HQL
NativeQL
//1+n问题
publicvoidtestQuery(){
Sessions=sf.openSession();
s.beginTransaction();
Listtopics=
(List)s.createQuery(“fromTopic”).list();
//Listtopics=
(List)s.createCriteria(Topic.class).list();
for(Topict:
topics){
System.out.println(t.getName()+"-"+t.getTitle());
}
s.getTransaction().commit();
s.close();
}
1+n问题就是:
我想得到topic里面的东西,这个只要发一个select语句就可以了
(1)。
但是topic和category设置了ManyToOne并且默认的fetch=FetchType.eager.所以就把关联的category也