您的位置:首页 > 其它

Hibernate-note02

2015-07-30 22:13 543 查看
hibernate

 查询分为单表和多表查询

一.单表查询

1.HQL---官方推荐的方式

01.查找所有
String hql="from User";
//这个语句中是from后面加上类名,切记不是表名
Query query=sess.createQuery(hql);//创建一个query对象,传入hql
List<User> userlist=query.list();//右边不是一个泛型集合所以会有警告线
//当返回值是多条结果的时候就用query.list()
//当时一条记录的时候用query.uniqueResult()
for(User u:userlist){
System.out.println(u.getName());
}

//本质上是我们写了一个hql,hibernate会根据它生成对应的sql语句

//它和任何数据库是没有关系,

//hibernate拿到hql之后,会根据配置的方言标识,自动的翻译成哪种数据语言

02.where

String hql="from User where name='tom'";

//这里写的where后面的那么是属性的名字不是列名
Query query=sess.createQuery(hql);
List<User> userlist=query.list();

for(User u:userlist){
System.out.println(u.getId());
}

当然在HQL中也可以使用别名:

String hql="from User as u where u.name='tom'";

记得要是u.name,但是别名对生成的sql语句是没有影响的

03.过滤条件

在where语句中还可以使用各种过滤条件,如:=、<>、<、>、>=、<=、between、not between、in、not in、

is、like、and、or等。
• from Student where age > 20;
• from Student where age between 20 and 30;
• from Student where name is null;
• from Student where name like ‘小%’;
• from Student where name like ‘小%’ and age < 30
• from Student where name like ‘t_m’ and age < 30

04.获取一个不完整的对象

04.1查询出来一列的时候,返回的结果就是一个String的list,若是id就是Integer

String hql="select name from User";//这里写的where后面的那么是属性的名字不是列名
Query query=sess.createQuery(hql);
List<String> userlist=query.list();

for(String u:userlist){
System.out.println(u);
}

04.2查询的是多列的时候,返回的是一个对象数组,Object[],下标0装查询的第一列,1--第二列

String hql="select id,name,age from User";/
Query query=sess.createQuery(hql);
List<Object[]> userlist=query.list();

for(Object[] u:userlist){
System.out.println(u[0]+":"+u[1]+" - "+u[2]);
}

//若是想让返回的是一个对象,那就不要写select直接用from User。若是写了select,即使

//所有属性都查询了,仍然是一个Object[],不是一个对象

05.

除了id查询,若是根据id查询,就直接用get/load就行了,这样性能好一些。

//当时查询时候返回的是一条记录的时候用query.uniqueResult()

String hql="select id,name,age from User where name='tom'";//这里写的where后面的那么是属性的名字不是列名
Query query=sess.createQuery(hql);
User user=(User) query.uniqueResult();

System.out.println(user.getId());

06.统计和分组查询

统计:返回的是一条记录,并且是long型不是int型
eg1:
String hql="select count(*) from User where name='rose'";
Query query=sess.createQuery(hql);
long result=(Long) query.uniqueResult();

System.out.println(result);

eg2:
String hql="select name,count(*),sum(age) from User group by name";
Query query=sess.createQuery(hql);
List<Object[]> userlist=query.list();

for(Object[] u:userlist){
System.out.println(u[0]+":"+u[1]+" - "+u[2]);
}

聚合函数:
String hql="select count(*),max(age)from User where name='rose'";//
Query query=sess.createQuery(hql);
Object[] result=(Object[]) query.uniqueResult();
//当多于一列的时候,它就不能识别是long类型,就得用Object[]数组接收
System.out.println(result[0]+":"+result[1]);

更多一些类似的写法

• select distinct name from Student;

• select max(age) from Student;

• select count(age),age from Student group by age;

• from Student order by age;

07.占位符--?

在mysql中给第几个?赋值,mysql是从1开始,hibernate是从0开始
String hql="select count(*),sum(age) from User where name=?";
Query query=sess.createQuery(hql);

query.setString(0, "rose");//是什么类型就是set+类型/setInt,但是没有setObject
Object[] result=(Object[]) query.uniqueResult();

System.out.println(result[0]+":"+result[1]);

但是要是还有一个是query.setParameter(0, "rose");它就是传进去的是一个对象,

          所以不用管它是什么类型了
//和query.setString(0, "rose");功能一样

08.引用占位符

//用引用占位符就是需要在name=后面加上个:,冒号后面的名字可以直接任意取名,没有引号

String hql="select count(*),sum(age) from User where name=:name";
Query query=sess.createQuery(hql);

//在用的时候就是在这里加上这句
query.setParameter("name", "rose");
//query.setString("name", "rose");//这样也可以
//query.setParameter(0, "rose");//但是这样就会出错,已经没有了位置的识别
Object[] result=(Object[]) query.uniqueResult();

System.out.println(result[0]+":"+result[1]);

09.分页
String hql = "from User ";
Query query = sess.createQuery(hql);

query.setFirstResult(5);//从第几条记录开始
query.setMaxResults(5);//一页有几条记录
//这样写之后,hibernate会根据方言标识自动写sql语句
List<User> userlist = query.list();// 右边不是一个泛型集合所以会有警告线

for (User u : userlist) {
System.out.println(u.getId());
}

2.基于面向对象查询

Criteria查询

Criteria对象提供了一种面向对象的方式查询数据库。Criteria对象需要使用Session对象来获得

一个Criteria对象表示对一个持久化类的查询

01.只需创建一个Criteria,反射出User类,然后查询就可以了

Criteria c = sess.createCriteria(User.class);
List<User> userlist = c.list();
for (User u : userlist) {
System.out.println(u.getId());
}

02.where

方法名称 对应SQL中的表达式
Restrictions.eq      →  → field = value
Restrictions.gt      →  → field > value
Restrictions.lt      →  → field < value
Restrictions.ge      →  → field >= value
Restrictions.le      →  → field <= value
Restrictions.between →  → field between value1 and value2
Restrictions.in      →  → field in(…)
Restrictions.and     →  → and
Restrictions.or      →  → or
Restrictions.like    →  → field like value

02.1,and

//若是查询的是一个条件就写一个就好,多个就像下面那样

Criteria c = sess.createCriteria(User.class);
c.add(Restrictions.eq("name","tom"));
c.add(Restrictions.eq("age", 23));//两个条件

List<User> userlist = c.list();
for (User u : userlist) {
System.out.println(u.getId());
}

02.2,or
Criteria c = sess.createCriteria(User.class);
c.add(Restrictions.or(Restrictions.eq("age", 23),Restrictions.eq("name","tom")));

//若是有三个条件就可以把上面这句话再嵌套到一个条件里
List<User> userlist = c.list();
for (User u : userlist) {
System.out.println(u.getId());
}

03.like
c.add(Restrictions.like("userName", "J"));
c.add(Restrictions.eq("id", 120));

04.分组统计

//此时若是c.setProjection(Projections.sum("age"));
 c.setProjection(Projections.avg("age"));

//这两条同时,不支持,会只查询第2个avg

Criteria c = sess.createCriteria(User.class);
c.setProjection(Projections.sum("age"));

Object obj = c.uniqueResult();
System.out.println(obj);

05.Projections对象

方法名称 描述

Projections.sum           等于SQL中聚合函数sum

Projections.avg           等于SQL中聚合函数avg

Projections.count         等于SQL中聚合函数count

Projections.max           等于SQL中聚合函数max

Projections.min           等于SQL中聚合函数min

Projections.distinct       去除重复记录

Projections.groupProperty 对指定的属性进行分组查询

06.多个统计与分组

//多个的话要先创建一个ProjectionList集合
session.beginTransaction();
Criteria c = session.createCriteria(User.class);

ProjectionList list = Projections.projectionList();
list.add(Projections.sum("id"));
list.add(Projections.min("id"));
c.setProjection(list);

Object[] obj = (Object[]) c.uniqueResult();
System.out.println("sum:" + obj[0]);
System.out.println("min:" + obj[1]);

07.排序

//sql中直接加orderby就可以了,这里用addOrder

Criteria c = session.createCriteria(User.class);

c.addOrder(Order.desc("name"));

List<User> list = c.list();
for(User user : list){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

当有多个排序条件的时候,加上就好了
Criteria c = sess.createCriteria(User.class);

c.addOrder(Order.desc("name"));
c.addOrder(Order.asc("id"));

List<User> list = c.list();
for (User user : list) {
System.out.println(user.getName()+":"+user.getId());
}

3.原生的sql语句

这时候hibernate不再翻译了

优点:提高性能,不用打开数据库连接和关闭连接

缺点:延迟加载不能用,缓存不能用,移植性不能用

01.原生查询的返回的是一个对象数组

String sql="select * from t_user";
SQLQuery query=sess.createSQLQuery(sql);
List<Object[]> list=query.list();
for(Object[] obj:list){
System.out.println(obj[0]);
}

02.有一个特性当查询的列是全字段的,和某个类的属性是一一对应的,可以用addEntity(User.class);

来返回一个对象,缺一个都不行,缺一个属性就返回的是数组

String sql="select * from t_user";
SQLQuery query=sess.createSQLQuery(sql).addEntity(User.class);
List<User> list=query.list();
for(User obj:list){
System.out.println(obj.getId());
}

03.返回的结果是一条记录的时候,用这句话User user = (User) query.uniqueResult();
session.beginTransaction();
String sql = "select id,username,userpwd from t_user where id = 2";
SQLQuery query = session.createSQLQuery(sql).addEntity(User.class);
User user = (User) query.uniqueResult();
System.out.println(user.getUserName());
session.getTransaction().commit();

04.其实这时候用原生的sql语句时候是可以进行多表查询的,它和hibernate方言和翻译都没有什么关系了

二.多表查询

数据关联映射

• 一对一

• 一对多

• 多对多

1.一对多和多对一

在多的这边存的是一这边的主键
多              一

        t_address       t_user

        id    id

        t_address       name

        userid(Fk)      age

        tel

2.配置多表查询,

  01.建两个实体类,
在多表的实体类中添加单表的那个类的实例,生成set/get
这时候获取的时候只需要getUser().getName();
public class Address {
private int id;
private String address;
private int tel;
private User user;
//get set method
}

在单表的实体类中添加多表的那个类的set集合,生成set/get
获取的时候只需要user.getAddressSet();然后迭代获取即可
public class User {
private int id;
private String name;
private int age;
private Set<Address> addressSet;//在这里存的是一个set集合,不是list集合,切记
//get set method
}

上面的是双向的配置,如果我们只需要知道一个user的多个地址,而不需要知道地址对应的user
就不需要有,不需要配置private User user;

  02.配置
001.添加address。hbm。xml文件
<hibernate-mapping package="com.kaishengit.pojo">
<class name="Address" table="t_address">
<id name="id" column="id">
<generator class="native"></generator>//主键生成策略
</id>
<property name="tel" column="tel"></property>
<property name="address"></property>
<many-to-one name="user" class="User" column="user_id"></many-to-one>
//name是属性,class要和package组成一个完全限定名,要是没有package,这里就要写全名
//column是数据里的表名

</class>
</hibernate-mapping>

     002.修改user.hbm.xml文件
<hibernate-mapping package="com.kaishengit.pojo">

<class name="User" table="t_user">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<property name="age"></property>

//因为新增加了一个set集合,所以配个set
<set name="addressSet">//set集合的名字
<key column="user_id"></key>//数据库中外键的名字,
//作用看用户有几个地址,根据user_id找,在Address这个文件中
//Address对应的有一个映射文件,然后映射文件中有一个数据库表
//数据库表中有这个user_id
<one-to-many class="Address" />//一对多对应的类名
</set>

</class>

</hibernate-mapping>

//数据库中外键的名字,
//作用看用户有几个地址,根据user_id找,在Address这个文件中
//Address对应的有一个映射文件,然后映射文件中有一个数据库表
//数据库表中有这个user_id

003.在hibernate.cfg.xml中加上一个映射文件的配置
<mapping resource="com/kaishengit/pojo/Address.hbm.xml"/>

至此配置完成

3.保存,insert
01.
session.beginTransaction();

User user=new User();
user.setName("tom");
user.setAge(23);

Address a1=new Address();
a1.setTel("15822");
a1.setAddress("America");

Address a2=new Address();
a2.setTel("15689");
a2.setAddress("China");

a1.setUser(user);
a2.setUser(user);

session.save(user);
session.save(a1);
session.save(a2);

session.getTransaction().commit();

Hibernate: insert into t_user (name, age) values (?, ?)
Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)
Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)

这是是正确的,性能最好的,先存一,再存多

02.
a1.setUser(user);
a2.setUser(user);

session.save(a1);
session.save(a2);
//存之前user是自由态

session.save(user);
//存后变成了持久态
//这时候hibernate中a1和a2检测到与之相关联的user状态发生了改变所以执行了update

Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)
Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)
Hibernate: insert into t_user (name, age) values (?, ?)
Hibernate: update t_address set tel=?, address=?, user_id=? where id=?
Hibernate: update t_address set tel=?, address=?, user_id=? where id=?

这时候在存a1和a2的时候,user_id为空,
user存完后,user_id有值了,然后为了维护两个表的关系,所以执行后面的两个update

03.热心群众不能太多

//user有哪些address
Set<Address> set=new HashSet<Address>();
set.add(a1);
set.add(a2);
user.setAddressSet(set);//user关联的a1,a2在关联的时候是自由态,
//而在save(a1,a2);之后,user发现两个对象状态改变了
//然后执行update同步到数据库

//address有哪些user
a1.setUser(user);
a2.setUser(user);

session.save(user);
session.save(a1);
session.save(a2);

Hibernate: insert into t_user (name, age) values (?, ?)

Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)

Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)

Hibernate: update t_address set user_id=? where id=?

Hibernate: update t_address set user_id=? where id=?

这个时候双向都告诉了,这时多余的,insert的user是有id的,后面update是没有用的

Address a2=new Address();
a2.setTel("15689");
a2.setAddress("China");

Set<Address> set=new HashSet<Address>();
set.add(a1);
set.add(a2);

user.setAddressSet(set);
a1.setUser(user);
a2.setUser(user);

session.save(a1);
session.save(a2);
session.save(user);

Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)

Hibernate: insert into t_address (tel, address, user_id) values (?, ?, ?)

Hibernate: insert into t_user (name, age) values (?, ?)

Hibernate: update t_address set tel=?, address=?, user_id=? where id=?

Hibernate: update t_address set tel=?, address=?, user_id=? where id=?

Hibernate: update t_address set user_id=? where id=?

Hibernate: update t_address set user_id=? where id=?

这时就是两个address进去数据库了发现user状态改变了,执行两条update

user发现address也发生了改变,执行两条update

这个是性能最低的时候

NOTE:解决方案

01.先存一后存多

02.双方只让一方关联这个关系

04.让多的来维护关系,一来放弃维护关系(校长和学生)

在一的配置文件中加上一句配置

在user.hbm.xml中加上:inverse="true"(默认是false,逆=true的时候是放弃关系维护)
<set name="addressSet" inverse="true">
<key column="user_id"></key>
<one-to-many class="Address" />
</set>

eg1:   Set<Address> set=new HashSet<Address>();
set.add(a1);
set.add(a2);
user.setAddressSet(set);
这个时候上面这段就没有作用了
a1.setUser(user);
a2.setUser(user);

session.save(a1);
session.save(a2);
session.save(user);

性能最高的解决方案:

01.先存一再存多

02.让一的一方放弃维护关系

4.删除一表中数据

因为一对多,一中的数据在多中是外键,不能直接删除

这时,若是想删除一中的数据时候,能够同时删除多中对应的外键的记录

只需在一的配置user.hbm.xml中加上映射设置级联删除:cascade="delete"

<set name="addressSet" inverse="true" cascade="delete">
<key column="user_id"></key>
<one-to-many class="Address" />
</set>

这时在直接删掉了,不加配置的会出错

User user=(User) session.get(User.class, 9);
session.delete(user);

但是这个在用的时候要注意业务是不是需要级联删除

例如:学生--老师,老师辞职了,学生也没有了。这就是错的。

      但是在帖子的时候,帖子删了,评论也没了。这就是对的。

5.查询
session.beginTransaction();
User u = (User) session.load(User.class, 2);
System.out.println(u.getName());

Set<Address> set = u.getAddressSet();
for(Address add : set){
System.out.println(add.getAddress());
}
session.getTransaction().commit();

这时候其实也是一个懒加载,在执行的时候,当需要address的时候它才会执行查找address。

6.禁止懒加载--抓取策略

假如每次用到user,就会用到address

这时候可以禁止懒加载,在配置中加上,抓取策略fetch="join"

<set name="addressSet" inverse="true" cascade="delete" fetch="join">
<key column="user_id"></key>
<one-to-many class="Address" />
</set>

查询的时候:

User u = (User) session.load(User.class, 10);
System.out.println(u.getName());

这个时候再查询的时候,一句话就把user和address都查询出来

Set<Address> set = u.getAddressSet();
for(Address add : set){
System.out.println(add.getAddress());
}

当set的时候,不会再用sql语句执行

当如果大量的使用查询就需要全部查出来就用这个特性,如果不是就不要用,这是提高性能的一个点

7.排序

set:不重复的,hashset是无序的,其他的set是有序的

所以如果输出的时候想要是有序的

在配置中加上,一个排序order-by="id desc",会按照id进行降序排列

若是有多个项就order-by="id,name,xxx desc",用逗号隔开

<set name="addressSet" inverse="true" cascade="delete" fetch="join" order-by="id desc">
<key column="user_id"></key>
<one-to-many class="Address" />
</set>

这时候在输出的结果上在sql语句最后加上order by id desc

8.根据多(address),来找一(user)
Address address=(Address) session.get(Address.class, 22);
System.out.println(address.getAddress());//执行查询地址
System.out.println(address.getUser().getName());//执行查询用户

同样的懒加载,用到user的时候才会执行第二条sql语句

02.若是想要查询地址的时候,就同时获得了user

只需要在address.hbm.xml中配置:抓取策略-即可

<many-to-one name="user" class="User" column="user_id" fetch="join"></many-to-one>

但是要根据业务来选择。

例如:购物车的地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hibernate