(1)配置hibernate-cfg.xml
<hibernate-configuration>
<session-factory>
<!-- hibernate的方言,用来确定连接的数据库 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 数据库的连接类 -->
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- 数据库的连接字符串和用户名密码 -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/itat_hibernate
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">
123456
</property>
<!-- 在使用hibernate时会显示相应的SQL -->
<property name="show_sql">true</property>
<!-- 会自动完成类到数据表的转换 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 加入实体类的映射文件 -->
<mapping resource="org/zttc/itat/model/User.hbm.xml"/>
<mapping resource="org/zttc/itat/model/Book.hbm.xml"/>
</session-factory>
</hibernate-configuration>
(2)创建实体映射文件:
(略)
(3)代码编写:创建SessionFatory:
SessionFactory是线程安全的,所以SessionFatory要基于单利模式来创建。
@Test
public void test01() {
Configuration cfg = new Configuration().configure();
//cfg.buildSessionFactory();
//在hibernate3中都是使用该种方法创建,但是在4中被禁用了,使用以下方法
ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).buildServiceRegistry();
SessionFactory factory =
cfg.buildSessionFactory(serviceRegistry);
Session session = null;
try {
session = factory.openSession();
//开启事务
session.beginTransaction();
User u = new User();
u.setNickname("张三");
session.save(u);
//提交事务
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
if(session!=null) session.close();
}
}
【总结】:
临时状态 à 持久状态:做save操作;
离线状态 à 临时状态:做delete操作;
有时候不知道对象是离线还是临时状态,这时候可以调用session的saveandupdate()方法
l Transient:是指临时状态,即new出一个对象,但是数据库中还没有这个对象;
l Persistent:new一个对象并save后就是Persistent状态(持久化状态);
l Detached:当session关闭并且对象保存在了数据库中,这时候就是离线状态。
关于Persistent:
@Test
public void testTransient() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
u.setBorn(sdf.parse("1976-2-3"));
u.setUsername("zxl");
u.setNickname("赵晓六");
u.setPassword("123");
//以上u就是Transient(瞬时状态),表示未被session管理且数据库中没有
//save之后,被session所管理,且数据库中已经存在,此时就是Persistent状态
session.save(u); //u变成持久化状态,被sessio管理,正常保存
u.setNickname("赵晓其");//更新缓存内容,提交时候更新数据库
//如果没有提交那么上面的(从save开始)就是持久化状态,session中有完整的对象的//信息处于持久化状态的对象设置了新数据,提交的时候就会做更新操作
session.getTransaction().commit();//导致更新操作执行
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
继续解释:
@Test
public void testPersistent02() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
u.setBorn(sdf.parse("1976-2-3"));
u.setUsername("zxq");
u.setNickname("赵晓八");
u.setPassword("123");
//save后缓存(session)保存这个u对象信息
session.save(u);
u.setPassword("222");//不会导致缓存中内容变化
//缓存内容没有变化,所以不会执行(这部分有些不明白??)
session.save(u);
u.setNickname("赵晓吧");
//下面语句不执行(持久化状态的update不执行,提交时候才会执行update)
session.update(u);
u.setBorn(sdf.parse("1988-12-22"));
//没有意义
session.update(u);
//执行update
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
load出来的对象也是持久化对象:
//此时u是Persistent
User u = (User)session.load(User.class, 10);
@Test
public void testDetach05() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
//u.setId(110);
u.setNickname("abc");
//如果u是离线状态就执行update操作,如果是瞬时状态就执行Save操作
//但是注意:该方法并不常用
session.saveOrUpdate(u);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
当使用load加载一个对象的时候,如果取出的对象没有使用,那么就不会发sql——这既是Hibernate的“延迟加载(懒加载)”。且,该对象其实是一个代理对象,该对象中只有一个id的值,若打印该id也不会发sql(不用从数据库中取)。
@Test
public void testLazy02() {
Session session = null;
try {
session = HibernateUtil.openSession();
User u = (User)session.load(User.class, 1);
//此时一条sql都没有发,这就是hibernate的延迟加载
/**延迟加载指的就是,当完成load操作之后,并不会马山发出sql语句,只有在使用到该对象时才会发出sql,当完成load之后,u其实是一个代理对象,这个代理对象中仅仅只有一个id的值*/
//此时不会发sql
System.out.println(u.getId());
//nickname没有值,必须去数据库中取。所以会发出sql
System.out.println(u.getNickname());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【例】:
@Test
public void testLazy04() {
Session session = null;
try {
session = HibernateUtil.openSession();
//get是只要一执行就会发出sql,get没有延迟加载
User u = (User)session.get(User.class, 101);
//get的时候发现数据库中并没有该数据,所以u是null,打印u.getId(),会抛出空指针异常,如果是使用load的话,会打印101(代理对象存储了id的值)
System.out.println(u.getId());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【例】:
@Test
public void testLazy05() {
Session session = null;
try {
session = HibernateUtil.openSession();
User u = (User)session.load(User.class, 101);
//由于id已经存在,所以不会抛出异常
System.out.println(u.getId());
//此时会去数据库中取数据,发现没有这个对象,但是u并不是空,所以会抛出ObjectNotFoundException
System.out.println(u.getNickname());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【情景演示】:
Dao中的load方法:
public User load(int id) {
Session session = null;
User u = null;
try {
session = HibernateUtil.openSession();
u = (User)session.load(User.class, 1);
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
return u; //这里返回的仅是一个代理对象,且此时session已经关闭
}
控制器中调用这个方法用来在页面显示:
@Test
public void testLazyQuestion() {
UserDao ud = new UserDao();
/*由于使用了load,load是有延迟加载的,返回的时候的u是一个代理对象,仅仅只有一个id
* 但是在返回的时候Session已经被关闭了,此时当需要使用u的其他属性时就需要去数据库中
* 但是Session关闭了,所以就抛出org.hibernate.LazyInitializationException no session
*/
User u = ud.load(2);
System.out.println(u.getUsername());
}
解决方法:
方法1:使用get方法;
方法2:在过滤器中获取session放到context中,然后在过滤器中关闭。Spring中使用openSessionInView。
<hibernate-mapping package="org.zttc.itat.model">
<class name="Book" table="t_book">
<id name="id">
// assigned:表示要手动指定主键
// uuid:表示自动生成一个uuid字符串,所以主键必须是String
// native:也是自动生成,生成的是1、2、3等有序列,所以检索会快些
// native的缺点是每次插入一条数据库都会查询一下数据库,开发中查询
// 操作会比较多,所以开发中会使用native
<generator class="uuid"/>
</id>
<property name="name"/>
<property name="price"/>
<property name="bookPage" column="book_page" type="int"/>
</class>
</hibernate-mapping>
【提示】:
l 实体字段名,比如bookName,映射到数据库最好映射成book_name
<property name="bookPage" column="book_page" type="int"/>
l 在实际开发中,一般不会使用Hibernate来生成表,一般的开发中,都是首先用PowerDesigner设计好数据库,然后生成SQL语句,然后用这个SQL语句来生成表。
这样的好处是,在开发数据库的时候可以为数据库创建索引和视图。
l 对效率要求不高的项目适合使用Hibernate;
l 对效率比较高的项目,如果使用Hibernate的话,若想要效率也高,则可以这么做:增删改使用Hibernate,而查询使用原生态的SQL。
l 写Hibernate语句时,比如清楚一句Hibernate代码执行了几条SQL语句,否则性能可能大降!
l 关系一般是由“多”的一方来维护;
<!-- inverse=true表示不在自己这一端维护关系 -->
<set name="stus" lazy="extra" inverse="true">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
l 添加的时候,一般都是先添加“一”的一方;
l 懒加载会多发一条SQL语句(抓取策略可以只发一条SQL语句……?)
l 关联(cascade):不要在“多”的一方使用关联。因为,比如,删除多的一方的一条记录,同时会删除“一”的一方对应的记录,但是如果“一”的一方的该条记录又关联了其他记录的话,就不能删除了。使用cascade的情况一般是:在“一”的一方删除时使用,特殊情况才会在add上做级联。
<property name="title"/>
<property name="content"/>
<!-- 使用了lazy=extra之后会稍微智能一些,会根据去的值的不同来判断是调用count和获取投影 ,比如取出总条数一般情况下会取出所有数据的list然后计算list的size,但是用了extra后就会直接count(*)-->
<set name="comments" lazy="extra">
<!-- key用来指定在对方的外键的名称 -->
<key column="mid"/>
<!-- class用来设置列表中的对象类型 -->
<one-to-many class="Comment"/>
</set>
</class>
(略)
l 一个Hibernate项目是使用注解还是Annotation呢?
——小项目使用注解,大项目(一两百万行代码的)使用xml。因为大项目的类比较多,看xml文件更方便。大项目不用外键的方式????
@Entity
@Table(name="t_classroom")
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
// mappedBy表示关系由对方的classroom属性维护,如果没有mappedBy的话,会生
// 成中间表
@OneToMany(mappedBy="classroom")
// 使用extra后sql语句会较智能,比如计算条数会自动使用count(*)
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<Student> getStus() {
return stus;
}
public void setStus(Set<Student> stus) {
this.stus = stus;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
}
@Entity
@Table(name="t_student")
public class Student {
private int id;
private String name;
private String no;
private Classroom classroom;
@Id
@GeneratedValue
public int getId() {
return id;
}
// fetch的默认值是EAGER,表示不使用懒加载,一条语句就能把相关的数据查询
// 出来;如果值设置为LAZY的话,就会使用懒加载,会多发一条sql
@ManyToOne(fetch=FetchType.LAZY)
// 在自己这边生成的外键名是cid,哪一方维护关系就在哪一方加JoinColumn(就是
// 外键的意思)一般都由“多”的一方维护关系,即在自己这方配置对方的外键。
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
(主要内容就是使用Extra的问题,查询属于教室的学生的数目)。
以上都不需要中间表,而这个“多对多”关系需要中间表。
【提示】:
多对多关系开发中一般不用,而是使用两个一对多关系来代替。
@Entity
@Table(name="t_admin")
public class Admin {
private int id;
private String name;
private Set<Role> roles;
@Id
@GeneratedValue
public int getId() {
return id;
}
// 设置由对方维护关系
@ManyToMany(mappedBy="admins")
public Set<Role> getRoles() {
return roles;
}
}
@Entity
@Table(name="t_role")
public class Role {
private int id;
private String name;
private Set<Admin> admins;
public Role() {
admins = new HashSet<Admin>();
}
public void add(Admin admin) {
admins.add(admin);
}
@ManyToMany(mappedBy="admins")
public Set<Role> getRoles() {
return roles;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
// joinColumns:用来设置自己在中间表的外键名;inverseJoinColumns用来设置
// 对方在中间表的外键名
@ManyToMany
@JoinTable(name="t_role_admin",
joinColumns={@JoinColumn(name="rid")},
inverseJoinColumns={@JoinColumn(name="aid")})
public Set<Admin> getAdmins() {
return admins;
}
public void setAdmins(Set<Admin> admins) {
this.admins = admins;
}
}
专业-教室:1对多
教室-学生:1对多
@Entity
@Table(name="t_stu")
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
//LAZY就是XML中的select,EAGER就表示XML中的join
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
//由多的一方维护关系
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.SUBSELECT)
public Set<Student> getStus() {
return stus;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
@Entity
@Table(name="t_special")
public class Special {
private int id;
private String name;
private String type;
private Set<Classroom> clas;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="special")
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<Classroom> getClas() {
return clas;
}
}
略,重点提示:
(1) 设置<set>的时候,使用inverse,表示不在自己这边维护关系,如下配置classroom:
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
/*基于?的条件的查询,特别注意:jdbc设置参数的最小下标是1,hibernate是0*/
List<Student> stus = session
.createQuery("from Student where name like ?")
.setParameter(0, "%李%")
.list();
/*还可以基于别名进行查询,使用:xxx来说明别名的名称,基于列表查询一定要用别名*/
List<Student> stus = session
.createQuery("from Student where name like :name and sex=:sex")
.setParameter("name", "%刘%")
.setParameter("sex", "男")
.list();
/*使用uniqueResult可以返回唯一的一个值注意返回值类型(uniqueResult的返回类型是Object)*/
Long stus = (Long)session
.createQuery("select count(*)
from Student where name like :name and sex=:sex")
.setParameter("name", "%刘%")
.setParameter("sex", "男")
.uniqueResult();
/*基于投影的查询,通过在列表中存储一个对象的数组,注意返回值类型
基于投影的查询还可以基于DTO——DTO使用来传输数据*/
List<Object[]> stus = session
.createQuery("select stu.sex,
count(*) from Student stu group by stu.sex").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]);
}
【我的总结】:
group by的作用,select后的显示、计算是按照group by后进行的(group by后的记录相当于一张张字表,对这些字表进行select操作)。
/*如果对象中相应的导航对象,可以直接导航完成查询*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.classroom.name=? and stu.name like ?")
.setParameter(0, "计算机教育班")
.setParameter(1, "%张%")
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
/*可以使用in来设置基于列表的查询,此处的查询需要使用别名进行查询。
特别注意:使用in的查询必须在其他的查询之后*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.name like ? and stu.classroom.id in (:clas)")
.setParameter(0, "%张%")
.setParameterList("clas", new Integer[]{1,2})
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
【提示】:
基于列表查询要用别名:name。
/*可以通过is null来查询为空的对象,和sql一样不能使用=来查询null的对象*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.classroom is null")
.setFirstResult(0)
.setMaxResults(15)
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
/*使用对象的导航(内部使用Cross JOIN<笛卡尔积>)可以完成连接,但是是基于Cross JOIN,效率不高,可以直接使用JOIN(Join = Inner Join)来完成连接*/
List<Student> stus = ession
.createQuery("select stu from Student stu
left join stu.classroom cla where cla.id=2")
.setFirstResult(0)
.setMaxResults(15).list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
连接(Join)有三种:
(1) left join:
以下sql:t1(left)中不符合条件(t1.id=t2.cid)的也打印出来,无值的字段为null
select * from t_classroom t1
left join t_stu t2 on(t1.id=t2.cid)
(2) right join:同理(1)
(3) inner join(即join):两边都有的才显示。
左、右连接常用语统计数据。
// 以下sql语句显示不正常,本意是想显示每个班级的学生人数,但是没有学生的班级也会显示人数:
select t1.name,count(*) from t_classroom t1
join t_stu t2
on(t1.id=t2.cid) group by t1.id
// 改成以下sql:
select t1.name,count(t2.cid) from t_classroom t1
left join t_stu t2
on(t1.id=t2.cid) group by t1.id
l 查询每个班级的男生和女生的人数:
select t1.name,t2.sex,count(t2.cid)
from t_classroom t1
left join t_stu t2
on(t1.id=t2.cid)
group by t1.id,t2.sex
【提示】:
Hibernate的导航连接内部使用CROSS JOIN(全连接),效率很低!
l 查询2班的所有学生:
/*使用对象的导航可以完成连接,但是是基于Cross JOIN,效率不高,可以直接使用JOIN来完成连接*/
List<Student> stus = session
.createQuery("select stu from Student stu
left join stu.classroom cla where cla.id=2").setFirstResult(0)
.setMaxResults(15).list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
l 查询每个班的人数
/*使用对象的导航可以完成连接,但是是基于Cross JOIN,效率不高,可以直接使用JOIN来完成连接
思考:如果把classroom放前面呢?如果使用count(*)会如何?*/
List<Object[]> stus = session
.createQuery("select
cla.name,count(stu.classroom.id)
from Student stu
right join stu.classroom cla
group by cla.id").list();
for(Object[] stu:stus) {
System.out.println(stu[0]+","+stu[1]);
}
l 查询学生的信息、包括所在班级(班级名)、专业名
SQL_1:
List<Ojbet[]> stus =
session.createQuery(“select stu.id,stu.name,stu.sex,cla.name,spe.name
from Student stu
left join stu.classroom cla
left join cla.special spe”).list();
for( Object[] obj:stus ){
……
}
之前查询的字段,然后获取信息的时候,都是用Object[]接收返回值,然后通过Object的索引获取索引值的。但是实际开发中如果这样做就很不方便(如SQL_1),因为一一的取出索引值。这时候就可以使用DTO了,专门用来传输数据,没有任何存储意义。
>>>使用DTO解决以上问题:
新建一个DTO对象,要从数据库查询的哪些字段名就添加哪些属性名:
public class StudentDto {
private int sid;
private String sname;
private String sex;
private String cname;
private String spename;
get()…;
set()…;
}
HQL查询:
/*直接可以使用new XXObject完成查询,注意,一定要加上Object的完整包名这里使用的new XX,必须在对象中加入相应的构造函数*/
List<StudentDto> stus = session
.createQuery("select
new org.zttc.itat.model.StudentDto(
stu.id as sid,stu.name as sname,stu.sex as
sex,cla.name as cname,spe.name as spename)
from Student stu
left join stu.classroom cla
left join cla.special spe").list();
for(StudentDto stu:stus) {
System.out.println(stu.getSid()
+","+stu.getSname()+","+stu.getSex()+","
+stu.getCname()+","+stu.getSpename());
}
【提示】:
Criteria基本上在开发中不用!(我们项目组竟然用了……)
l 统计每个专业的学生的人数:
List<Object[]> stus = session.createQuery("
select spe.name,count(stu.classroom.special.id)
from Student stu
right join
stu.classroom.special spe
group by spe");
l 统计人数大于150的专业:
/*having是为group来设置条件的*/
List<Object[]> stus = session.createQuery("
select spe.name,
(count(stu.classroom.special.id))
from Student stu
right join
stu.classroom.special spe
group by spe
having count(stu.classroom.special.id)>150").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]);
}
l 统计每个专业的男女生人数:
/*having是为group来设置条件的*/
List<Object[]> stus = session.createQuery("
select stu.sex,spe.name
(count(stu.classroom.special.id))
from Student stu
right join
stu.classroom.special spe
group by spe,stu.sex").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]+","+obj[2]);
}
使用映射文件的情况下:
/*
(1)默认情况会发出3条SQL语句,一条取student,一条取Classroom,一条取Special
(3)通过设置XML中的<many-to-one name="classroom" column="cid" fetch="join"/>可以完成对抓取的设置
(4)如果使用Annotation的话,Hibernate会自动使用Join查询,只发出一条SQL
使用Annotation默认就是基于join抓取的,所以只会发出一条sql*/
session = HibernateUtil.openSession();
Student stu = (Student)session.load(Student.class, 1); System.out.println(stu.getName()+","+stu.getClassroom().getName()+","+stu.getClassroom().getSpecial().getName());
如果不想发出3条SQL,可以在映射文件中这么配置,使用fetch=”join”(默认值是select):
<class name="Student" table="t_stu">
<!-- <cache usage="read-only"/> -->
<id name="id">
<generator class="native"/>
</id>
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
专业是通过classroom来获取的,所以,同时要在classroom中配置fetch=”join”:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【警告】:
以上使用fetch=”join”会有这样的问题,就是,即使我query中不查询班级和专业,发出的SQL也会去查专业和学生!也就是说,使用了fetch=”join”后,延迟加载就不生效了!
/*使用fetch=join虽然可以将关联对象抓取,但是如果不使用关联对象也会一并查询出来这样会占用相应的内存*/
session = HibernateUtil.openSession();
Student stu = (Student)session.load(Student.class, 1);
//使用fetch=”join”,延迟加载就失效
System.out.println(stu.getName());
【对于Annotation的问题】:
因为在使用Annotation的情况下,默认就是基于join的抓取策略,所以,比如,我们只查学生的话,同时会把班级、专业信息都查出来,即把关联的信息都查出来。
【基于“多”的一方进行抓取:实例及解决方案】
session = HibernateUtil.openSession();
/**
* 在XML中配置了fetch=join仅仅只是对load的对象有用,对HQL中查询的对象无用,
* 所以此时会发出查询班级的SQL,解决的这个SQL的问题有两种方案,
* (1)设置对象的抓取的batch-size
* (2)在HQL中使用fecth来指定抓取
* 特别注意,如果使用了join fetch就无法使用count(*)
*/
List<Student> stus = session.
createQuery("select stu from Student stu).list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
XML配置:
// batch-size:值默认为1,这里设置成20,即一次抓取20个班;
// 缺点是占用内存较大,且session关闭后数据就丢失(??),这里可以看出
// Hibernate的一个缺点就是很容易影响性能
<class name="Classroom" table="t_classroom" batch-size="20 ">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
为了解决batch-size的问题,我们可以在xml中不使用batch-size,在HQL使用fetchy来指定抓取:
// 没有fetch进行查询会发出多条sql,有了fetch后只发一条
List<Student> stus = session.
createQuery("select stu from
Student stu join fetch stu.classroom").list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
【提示】:
因为HQL使用了join fetch,就无法使用count(*)了,如果还想使用count(*),可以将HQL格式化,将fetch替换成空。
【小结】:
抓取策略有两种:
(1) batch-size:在被抓取方的xml中配置,可以一次抓取大量的数据;
(2) HQL中使用fetch。
针对“一”的抓取一般不会使用双向关联,只会做“单向关联”。
l 取出班级和班级对应的学生
session = HibernateUtil.openSession();
Classroom cla = (Classroom)session.load(Classroom.class, 1);
/*此时会在发出一条SQL取class对象*/
System.out.println(cla.getName());
/*取学生姓名的时候会再发一条SQL取学生对象,可以在Classroom的配置文件中对学生属性添加fetch=”join”,就会用一条sql完成相关查询了*/
for(Student stu:cla.getStus()) {
System.out.println(stu.getName());
}
classroom的xml配置文件中加入fetch=join:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="join">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
l 取出一组班级
(classroom的xml配置还是使用实例1中的配置内容。)
List<Classroom> clas = session.createQuery("from Classroom").list();
for(Classroom cla:clas) {
System.out.println(cla.getName());
/*对于通过HQL取班级列表且获取相应学生列表时,fecth=join就无效了,这里,每次查询一个班级就会发出一个SQL语句,这样肯定有问题,解决方法:
(1)第一种方案可以设置set的batch-size来完成批量的抓取
(2)可以设置fetch=subselect,使用subselect会完成根据查询出来的班级进行一次对学生对象的子查询(推荐)*/
for(Student stu:cla.getStus()) {
System.out.print(stu.getName());
}
}
l 方法(1)的xml配置:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="join" batch-size="400">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【说明】:
这里设置batch-size=400后,虽然发出的SQL大大减少,但是,即使我们实际使用10条,Hibernate也会查询出400条,这样很耗内存!(实际怎么使用还是要根据项目实际情况来定)。
l 方法(2)的xml配置:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect" >
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【说明】:
这中方法只发出两条sql:查班级和查学生(subselect)。
另:这里好像<many-to-one>的fetch只有两个选项:join和select,而<set>的fetch多一个subselect。
@Entity
@Table(name="t_stu")
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
//LAZY就是XML中的select,EAGER就表示XML中的join
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.SUBSELECT)
public Set<Student> getStus() {
return stus;
}
//使用注解默认都是join查询的,所以这里的配置默认是:
//fetch=FetchType.EAGER,所以这里默认会将专业信息也查询出来
//所以这里要配置成LAZY
//但是,如果配置成LAZY后,如果去班级再取专业,就会多发一条SQL查询专业
//这个的解决方法有有两种:
//(1)在HQL中使用fetch join cla.special
//(2)
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
/* 基于Annotation由于默认的many-to-one的抓取策略是EAGER的,所以当抓取classroom时会自动发出多条SQL去查询相应的Special;
此时:
(1)可以通过join fecth继续完成对关联的抓取(这样SQL很容易变得很长,尤其是大项目中);
(2)直接将关联对象的fecth设置为LAZY(就是说不希望查出关联对象),但是(所以)使用LAZY所带来的问题是在查询关联对象时需要发出相应的SQL,很多时候也会影响效率(要查出关联对象的话还是要发出SQL的)
(效率是第一重要的!)*/
List<Student> stus = session.createQuery("select stu from " +
"Student stu join fetch stu.classroom cla join fetch cla.special").list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
session = HibernateUtil.openSession();
Classroom cla = (Classroom)session.load(Classroom.class, 1);
System.out.println(cla.getName());
/*此时会在发出两条SQL(取班级、取学生)取学生对象*/
for(Student stu:cla.getStus()) {
System.out.println(stu.getName());
}
只发出一条SQL的方法,配置classroom实体:
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.JOIN) //方法1:使用join
@Fetch(FetchMode.SUBSELECT) //方法2:使用subselect(同时会查专业,另外,即使只查一个班级也会发出两条sql语句进行查班级和查学生)
public Set<Student> getStus() {
return stus;
}
@ManyToOne(fetch=FetchType.LAZY) //使用lazy不关联不查询
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
实例1:基本查询:
/*此时会发出一条sql取出所有的学生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
实例2:使用Iterator展示N+1问题:
/*如果使用iterator方法返回列表,对于hibernate而言,它仅仅只是发出取id列表的sql。在查询相应的具体的某个学生信息时,会发出相应的SQL去取学生信息,这就是典型的N+1问题。
存在iterator的原因是,有可能会在一个session中查询两次数据,如果使用list每一次都会把所有的对象查询上来,而使用iterator仅仅只会查询id,此时所有的对象已经存储在一级缓存(session的缓存)中,可以直接获取*/
session = HibernateUtil.openSession();
Iterator<Student> stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
实例3:用Iterator从一级缓存(session)中取值
/*此时会发出一条sql取出所有的学生信息*/
//发出查class的sql
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*使用iterate仅仅只会取Student的id,此时Student的数据已经在缓存中,所以不会在出现N+1*/
//发出查id的sql
stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
实例4:实例3的变形(注意和实例3对比)
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*会发出SQL取完整的学生对象,占用内存相对较多*/
ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
【小结】:
(1) 多次在session中查询使用Iterato(此情况很少!);
(2) 一级缓存释义;
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student对象已经在session的缓存(一级缓存)中,此时就不会发sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
(3) 一级缓存中session关闭,一级缓存就关闭。
【提示】:
(1) 二级缓存是sessionFactory级别的缓存;
(2) 二级缓存是优化比较好、使用比较多的缓存。
实例1:演示一级缓存
@SuppressWarnings("unchecked")
public class TestCache {
@Test
public void test01() {
Session session = null;
try {
/*此时会发出一条sql取出所有的学生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student对象已经在session的缓存(一级缓存)中,此时就不会发sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
/*上一个Session已经关闭,此时又得重新取Student,这里会再发一条sql*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
实例2:使用二级缓存
使用二级缓存步骤:
步骤1:要在配置文件(Hibernate.cfg.xml)中配置,打开二级缓存。
<!-- 设置二级缓存为true -->
<property name="hibernate.cache.use_second_level_cache">
true
</property>
步骤2:加入二级缓存包(常用的是ehache-corte、Hibernate-ehcache,在Hibernate的optional目录下);
步骤3:要在配置二级缓存提供的类:
<!-- 设置二级缓存所提供的类 -->
<property name="hibernate.cache.provider_class">
net.sf.ehcache.hibernate.EhCacheProvider
</property>
步骤4:Hibernate4.0中还要配置工厂(目的是为了提高效率):
<!-- 在hibernate4.0之后需要设置facotory_class -->
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
步骤5:设置ehcache.xml文件,在该文件中配置二级缓存的参数:
从Hibernate的project目录中拷贝ehcache.xml配置文件到src目录下
<ehcache>
<diskStore path="java.io.tmpdir"/>
<!—默认缓存 ->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<!-- 每一个独立的cache可以单独为不同的对象进行设置
没有配置的就使用默认的->
<cache name="org.zttc.itat.model.Student"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/> -->
</ehcache>
步骤6:在Hibernate.cfg.xml中配置ehcache.xml配置文件的路径
<!-- 说明ehcache的配置文件路径 -->
<propertyname="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
步骤7:开启二级缓存
<hibernate-mapping package="org.zttc.itat.model">
<class name="Student" table="t_stu">
//这个使用了锁的机制。也可以是read-write,但是效率更低了。
<cache usage="read-only"/>
<id name="id">
<generator class="native"/>
</id>
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
</hibernate-mapping>
二级缓存使用实例:
/*此时会发出一条sql取出所有的学生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student对象已经在session的缓存(一级缓存)中,此时就不会发sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
//Student配置了二级缓存了,所以还能从缓存中取出数据
session = HibernateUtil.openSession();
/*上一个Session已经关闭,此时又得重新取Student*/
Student stu = (Student)session.load(Student.class, 1);
//因为二级缓存设置了read-only的话,若改变值stu.setName(“”)会报错
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
l 二级缓存缓存的是对象
二级缓存是把所有的对象都缓存到内存中,是基于对象的缓存。
【实例】:
try {
/*此时会发出一条sql取出所有的学生信息*/
session = HibernateUtil.openSession();
List<Object[]> ls = session
.createQuery("select stu.id,stu.name from Student stu")
.setFirstResult(0).setMaxResults(50).list();
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
session.beginTransaction();
/*以上代码仅仅取了id和name,而二级缓存是缓存对象的,所以上一段代码不会
将对象加入二级缓存此时就是发出相应的sql*/
Student stu = (Student)session.load(Student.class, 1);
//会报错,因为二级缓存设置为read-only
//stu.setName("abc");
System.out.println(stu.getName()+",---");
session.getTransaction().commit();
}
l 二级缓存和Iterator配合使用
Iterator的最主要作用就是在此了!
Session session = null;
try {
/*此时会发出一条sql取出所有的学生信息*/
session = HibernateUtil.openSession();
List<Object[]> ls = session
.createQuery("select stu from Student stu")
.setFirstResult(0).setMaxResults(50).list();
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
/*由于学生的对象已经缓存在二级缓存中了,此时再使用iterate来获取对象的时候,首先会通过一条取id的语句,然后在获取对象时去二级缓存中,如果发现就不会再发SQL,这样也就解决了N+1问题而且内存占用也不多*/
Iterator<Student> stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
}
l 如果使用list()如果想少发SQL就需要使用查询缓存:
session = HibernateUtil.openSession();
/*这里发出两条一模一样的sql,此时如果希望不发sql就需要使用查询缓存*/
List<Student> ls = session
.createQuery("select stu from Student stu")//HQL和上面的一样
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
查询缓存是针对HQL语句的缓存,查询缓存仅仅只会缓存id而不会缓存对象。
在Hibernate.cfg.xml中配置查询缓存:
<!--设置相应的查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
l 代码中的用法
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级缓存
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*会发出SQL取完整的学生对象,占用内存相对较多*/
ls = session.createQuery("from Student")
.setCacheable(true)
.setFirstResult(0).setMaxResults(50).list();
stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 开启查询缓存,查询缓存也是SessionFactory级别的缓存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
session = null;
try {
/*如果两条sql不一样,就不会开启查询缓存,查询缓存缓存的是HQL语句只有两个HQL完全一致(且参数也要一致)才能使用查询缓存*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 开启查询缓存,查询缓存也是SessionFactory级别的缓存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*查询缓存缓存的不是对象而是id,关闭二级缓存后很容易发出大量的sql*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 开启查询缓存,查询缓存也是SessionFactory级别的缓存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
session = null;
try {
/*查询缓存缓存的是id,此时由于在缓存中已经存在了这样的一组学生数据,但是仅仅只是缓存了id,所以此处会发出大量的sql语句根据id取对象,这也是发现N+1问题的第二个原因 所以如果使用查询缓存必须开启二级缓存*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 开启查询缓存,查询缓存也是SessionFactory级别的缓存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
配置实体中这么配置,其他的查询语句跟在xml中一样:
@Entity
@Table(name="t_stu")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
【小结】:
l 经常修改的对象不用二级缓存;
l 用list还是Iterator?如果对象取出来就用Iterator没有的话;
l 查询缓存缓存的是id,但是只有在hql是一样的情况下才使用查询缓存,所以一般不建议使用查询缓存;
在test01()方法中将id为1的学生取出来,并将名字修改为”Jack”,在test02()方法中,也将id为1的学生取出来,并将名字改为”Tom”,并且在对方commit之前完成改名操作,这样的话,名字不会被改掉。
所以,一般情况下,并发会导致更新丢失。
解决方法有两种:
(1) 悲观锁:
悲观锁是Hibernate基于数据库的机制实现的,但是在Hibernate3和Hibernate4它们的实现机制是不一样的——Hibernate3是基于同步的机制实现的(同步的大量的并发就容易导致效率的问题),所以在Hibernate4中就没有使用同步,如果两个同时修改同一个记录的话,就抛出异常。解释错误了,Hibernate3和Hibernate4的机制是一样的,都是同步的。
(2) 乐观锁:
乐观锁是在数据库中增加一个version的字段来实现的,每次修改都会让这个字段的数字增加1,在读取的时候根据Version这个版本的值来读取,这样如果并发修改就会抛异常。
<class name="Student" table="t_stu">
<cache usage="read-only"/>
<id name="id">
<generator class="native"/>
</id>
//数据库会增加一个version这个字段
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
(1) 在做关系时,尽可能使用单向关联,不要使用双向关联;
(2) 在大项目中(数据量超过百万条)使用Hibernate可以考虑以下几个原则(个人总结):
【原则1】:不要使用对象关联,尽可能使用冗余字段来替代外键(因为百万级别的数据使用跨表查速度会非常慢);
【基于冗余的关联-实例】:
l 上面的实例中,我们的学生表是和班级表关联的,但是,实际上,显示学生的信息时,一般都只是希望显示班级名称即可,并不需要更多的班级信息。
【设置冗余字段的方法:】
1) 给班级增加一个用当前时间毫秒级+随机数作为班级的主键;
2) 我们给Student实体增加班级编号(classBh)和班级名(className)这两个特冗余字段;这样我们在显示班级名的时候就不需要关联班级表了;
3) 同样,学生还需要专业信息,我们就再在Student实体中增加专业编号(speBh)和专业名称(speName),同时,班级实体中也增加专业的编号和专业的名称;
4) 使用冗余的缺点:比如,我们修改了班级名称的时候,学生表里的班级名称也要跟着做修改(但是性能基本没有影响)
Q:但是,如果有一个对象同时需要学生对象、班级对象、专业对象,那又怎么办呢?
——答案是使用DTO(数据传输对象)。
public class StudentDto {
private Student stu;
private Classroom cla;
private Special spe;
}
【原则2】:不使用HQL,而全部使用SQL;如果需要缓存,就使用自己的缓存,而不适用Hibernate的缓存(Hibernate的缓存在Session和SessionFatory会有耗内存)
(3)
public void test01() {
Session session = null;
try {
session = HibernateUtil.openSession();
List<Student> stus = session
.createSQLQuery("select * from t_stu where name like ?")
.addEntity(Student.class)
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
session = HibernateUtil.openSession();
//注意SQL语句上加了{},返回值用Object来接收,实例3中使用DTO接收
List<Object[]> stus = session
.createSQLQuery("select {stu.*},{cla.*},{spe.*} from
t_stu stu left join t_classroom cla
on(stu.cid=cla.id)
left join t_special spe on(spe.id=cla.spe_id)
where stu.name like ?")
.addEntity("stu",Student.class)
.addEntity("cla",Classroom.class)
.addEntity("spe",Special.class)
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
Student stu = null;
Classroom cla = null;
Special spe = null;
List<StuDto> list = new ArrayList<StuDto>();
for(Object[] obj:stus) {
stu = (Student)obj[0];
cla = (Classroom)obj[1];
spe = (Special)obj[2];
list.add(new StuDto(stu, cla, spe));
//这里会将学生姓名打印3遍,所以sql语句中加了花括号,用来自动将结果映射
//到前缀对应的对象中
System.out.println(stu.name+cla.name+spe.name);
}
l StudentDto实体:
public class StudentDto {
private int sid;
private String sname;
private String sex;
private String cname;
private String spename;
public StudentDto(int sid, String sname, String sex, String cname,
String spename) {
super();
this.sid = sid;
this.sname = sname;
this.sex = sex;
this.cname = cname;
this.spename = spename;
}
public StudentDto() {
}
}
l 查询
session = HibernateUtil.openSession();
//这里的sql为了和DTO对应,
//可以在select中new com.zmj…StudentDto(stu.id as sid,……)
List<StudentDto> stus = session.createSQLQuery("select
stu.id as sid,
stu.name as sname,
stu.sex as sex,
cla.name as cname,
spe.name as spename
from t_stu stu
left join t_classroom cla
on(stu.cid=cla.id) "
left join t_special spe
on(spe.id=cla.spe_id)
where stu.name like ?")
//这里第一感觉可能会想到使用setEntity,setEntity的参数必须是和数据库
//对应的,但是DTO明显不需要和数据库对应,所以这里使用以下这个方法:
.setResultTransformer(Transformers.aliasToBean(StudentDto.class))
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
for(StudentDto sd:stus) {
System.out.println(sd);
}
【提示】:
这样使用Hibernate的方法(查询使用sql,增、删、改使用Hibernate),效率会大大提高!