十一、Hibernate的检索策略
1、概述:
查询的时机:什么时候去查?
/**
* 一张表的检索策略我们称之为:
* 类级别的检索策略。
* 注意:只要是说类级别的检索策略,就一定不涉及关联对象。
*
* 类级别检索策略解决的问题:
* 1、查询的时机:
* 分为两种情况
* 立即加载:不管用不用,都马上查询出来
* 延迟加载:什么时候用,什么时候去查询。(懒加载,惰性加载) *
*常用方法:
* get:
* 永远都是立即加载。返回的当前实体类的对象。
* load
* 默认情况下是延迟加载。返回的当前实体类的代理对象。
* 它可以改为立即加载。
* 在对应的映射文件中,class元素使用lazy属性,把值改为false
* lazy取值:true 延迟加载(默认值)。false是立即加载。
* 例如:
* <class name="Customer" table="T_CUSTOMERS" lazy="false">
* query的list方法:
* 永远都是立即加载。
*
* @author zhy
*
*/
public class HibernateDemo1 { /*
* 不管用不用,都立即查询出一个客户的所有字段数据
*/
@Test
public void test1(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c1 = s.get(Customer.class, 1);
System.out.println(c1);
tx.commit();
s.close(); }
/*
* 什么时候用什么时候真正去查询
*/
@Test
public void test2(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c1 = s.load(Customer.class, 1);
System.out.println(c1);
tx.commit();
s.close(); }
}
2、类级别的检索策略
只影响Session的load()方法
Session.get()方法:永远都是立即加载。
/*
* 不管用不用,都立即查询出一个客户的所有字段数据
*/
@Test
public void test1(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c1 = s.get(Customer.class, 1);
System.out.println(c1);
tx.commit();
s.close(); }
Query.list()方法:立即检索。
Session.load()方法:默认是延迟加载。(load可以改为立即加载,lazy="false")
@Test
public void test2(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询所有客户
Query query = s.createQuery("from Customer");
List<Customer> cs = query.list();
//获取每个客户的所有订单
for(Customer c : cs){
System.out.println(c);
System.out.println(c.getOrders());
}
tx.commit();
s.close();
}
/*
* 什么时候用什么时候真正去查询
*/
@Test
public void test2(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c1 = s.load(Customer.class, 1);
System.out.println(c1);
tx.commit();
s.close(); }
3、关联级别的检索策略
概念:比如查询客户(类级别),所关联的订单的查询(关联级别)。
3.1、检索关联多的一方
应用场景:
一对多:根据客户查询订单
多对多:根据老师查询学生
配置:
<hibernate-mapping package="cn.itcast.domain">
<class name="Customer" table="T_CUSTOMERS">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="NAME"></property>
<property name="age" column="AGE"></property>
<!-- 一对多关系映射:
set元素:
作用:映射集合元素
属性:
name:映射实体类中的集合属性
table:指定对应的表
key元素:它是set的子元素
作用:就是用于指定外键的
属性:
column:指定外键字段的名称
one-to-many元素:它是set的子元素
作用:描述当前实体映射文件和set中指定属性之间的关系。
属性:
class:指定是从表的实体类名称
-->
<set name="orders" table="T_ORDERS" lazy="true" fetch="join">
<key column="CUSTOMER_ID"></key>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>
lazy:查询的时机(何时真正去查)。取值如下:
true:延迟加载
false:立即加载
fetch:查询的语句的形式。取值如下:
select:多条查询语句。
subselect:子查询。推荐
join:左外连接查询
说明:(必须搞明白)
序号 | lazy的取值 | fetch的取值 | 说明(都是以客户订单的一对多关系为例) |
1 | true(默认值) | select(默认值) | 时机:用时才真正去查询订单。 语句形式:有1条查询客户的和多条查询订单的select语句。 Batch-size:设置批量检索的深度。(建议3~10之间) |
2 | false | select(默认值) | 时机:不管用不用订单,查询客户时都立即查询订单。 语句形式:有1条查询客户的和多条查询订单的select语句。 Batch-size:设置批量检索的深度。(建议3~10之间) |
3 | extra | select(默认值) | 时机:用什么信息,查什么信息。只查询必要的。 语句形式:有1条查询客户的和多条查询订单的select语句。 batch-size:设置批量检索的深度。(建议3~10之间) |
4 | true | subselect | 时机:用时才真正去查询订单。 语句形式:子查询 batch-size:无效 |
5 | false | subselect | 时机:不管用不用订单,查询客户时都立即查询订单。 语句形式:子查询 batch-size:无效 |
6 | extra | subselect | 时机:用什么信息,查什么信息。只查询必要的。 语句形式:子查询 batch-size:无效 |
7 | true|false|extra | join(当join有效时,根本不看lazy属性) | 时机:无效。因为连接查询,一次就是两张表及以上。 语句:left outer join batch-size:无效 注意:Query查询会忽略join的存在。当join无效时,lazy就有效了。 |
/**
* 关联级别的检索策略
* 注意:
* 它永远说的是查询当前实体类时,要不要查询那个关联的对象。
* 涉及的是:一对多,多对一,多对多和一对一。
* 举例:
* 一对多和多对一
* 查询客户的时候要不要查询订单:一对多的情况
* 查询订单的时候要不要查询客户:多对一的情况
* 多对多
* 查询教师的时候要不要查询学生:多对多的情况
* 一对一
* 查询人员的时候要不要查询身份证号:一对一的情况
*
* 解决的问题:
* 1、查询的时机
* 立即加载:不管用不用,都马上查询出来
* 延迟加载:什么时候用,什么时候真正去查询
* 2、查询的方式(怎么查)
* 多条SQL语句
* 子查询
* 表连接
* 关于关联级别的检索策略,有如下配置
* 明确:是有少的一方,根据少的一方,去检索多的一方
* 举例说明:
* 我们有客户,根据客户获取订单。/ 我们有一个教师,根据这个教师获取学生
* 配置涉及的属性:
* lazy:加载的时机
* true:延迟加载(默认值)
* false:立即加载
* extra:极懒加载 (用什么查什么,不用的都不查)
* fetch:检索的方式
* select:多条SQL语句(默认值)
* subselect:子查询
* join:左外连接
*
* 我们现在是在映射文件的set元素上配置!
*
* @author zhy
*
* 第四种情况:lazy="true" fetch="subselect"
* <set name="orders" table="T_ORDERS" lazy="true" fetch="subselect">
*
*/
public class HibernateDemo5 { /*
* 需求:
* 查询所有客户,获取每个客户的所有订单
* 查询的时机:
* 延迟加载
* 查询的方式:
* 子查询
* select * from T_ORDERS where CUSTOMER_ID in (select id from T_CUSTOMERS )
* 注意:
* 当使用了子查询之后,batch-size属性就失效了。
*
*/
@Test
public void test2(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询所有客户
Query query = s.createQuery("from Customer");
List<Customer> cs = query.list();
//获取每个客户的所有订单
for(Customer c : cs){
System.out.println(c);
System.out.println(c.getOrders());
}
tx.commit();
s.close();
} /*当我们的fetch属性是subselect时,查询一个客户,并且获取该客户的订单,和取默认值没有区别。
* 因为:
* select * from t_orders where customer_id = 1;
select * from t_orders where customer_id in (1);
是一回事。
*
*
* 需求:
* 查询一个客户,获取该客户的所有订单
* 查询的时机:
* 查询的方式:
*/
@Test
public void test1(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询一个客户
Customer c1 = s.get(Customer.class, 1);//客户说的是类级别的检索策略
System.out.println(c1);
//获取当前客户的所有订单
System.out.println(c1.getOrders());
tx.commit();
s.close();
}
}
/**
* 关联级别的检索策略
* 注意:
* 它永远说的是查询当前实体类时,要不要查询那个关联的对象。
* 涉及的是:一对多,多对一,多对多和一对一。
* 举例:
* 一对多和多对一
* 查询客户的时候要不要查询订单:一对多的情况
* 查询订单的时候要不要查询客户:多对一的情况
* 多对多
* 查询教师的时候要不要查询学生:多对多的情况
* 一对一
* 查询人员的时候要不要查询身份证号:一对一的情况
*
* 明确: 有多的一方要不要查询少的一方
* 举例说明:
* 有订单,要不要查询客户/有人员,要不要查询身份证号。
* 解决的问题:
* 1、查询的时机
* 立即加载:不管用不用,都马上查询出来
* 延迟加载:什么时候用,什么时候真正去查询
* 2、查询的方式(怎么查)
* 多条SQL语句
* 表连接
* 关于关联级别的检索策略,有如下配置
* 配置涉及的属性:
* lazy:加载的时机
* false:立即加载。不管对方类级别的检索策略
* proxy:不确定。原因是要看对方类级别的检索策略。
* 如果对方类级别检索策略是延迟加载,则proxy就是延迟加载。
* 如果对方类级别检索策略是立即记载,则proxy就是立即加载。
* fetch:检索的方式
* select:多条SQL语句
* join:左外连接
*
* 我们以多对一为例:配置都是在many-to-one上!
* @author zhy
*
* 第二种情况:lazy=proxy fetch=join
* <many-to-one name="customer" class="Customer" column="CUSTOMER_ID" lazy="proxy" fetch="join"/>
*
*/
public class HibernateDemo11 { /* 当fetch取值是join时,一下就查询2张表。就不管lazy属性如何配置。
*
* 需求:
* 查询一个订单,根据订单获取该订单的客户
* 查询的时机:
* lazy属性失效
* 查询的方式:
* 左外连接
*
*/
@Test
public void test1(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//查询订单
Order o1 = s.get(Order.class, 1);
System.out.println(o1);
//根据订单,获取客户信息
System.out.println(o1.getCustomer());
tx.commit();
s.close();
}
}
3.2、检索关联少的一方
应用场景:(开发经验,少的一方,查出来就得了)
多对一:根据订单查询客户
一对一:根据人查询身份证
配置:
<hibernate-mapping package="cn.itcast.domain">
<class name="Order" table="T_ORDERS">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="ordernum" column="ORDERNUM"></property>
<property name="money" column="MONEY"></property> <!-- 多对一关系映射
使用的元素:many-to-one
属性:
name:指定的是在实体类中要映射的属性
class:指定该属性所对应的类
column:指定外键字段。
-->
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID" lazy="proxy" fetch="join"/>
</class>
</hibernate-mapping>
十二、检索方式
1、检索方式概述
a) 对象导航方式:通过对象间的引用关系。例如:customer.getOrder();
b) OID的检索方式:通过OID获取对象get和load。
c) HQL检索方式:使用面向对象的HQL查询语言。
HQL:Hibernate Query Language 类似SQL。表明换类名
d) QBC检索方式:Query By Criteria,更加面向对象。了解。
e) 本地SQL检索方式:原生态的JDBC+SQL语句。了解。
2、单表各种查询(HQL条件,QBC条件,QBC特例,原始SQL,多态,排序,具名,命名,分页)
/**
* hibernate中提供的查询方式
* @author zhy
*
*/
public class HibernateDemo1 { /*
* 对象导航查询
*
* 明确:
* 思想的转变。我们在需要某些数据时,是否还要单独去写一个方法来实现。
*
* 对象导航的思想
*/
@Test
public void test17(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Order o1 = s.get(Order.class, 1);
Customer c = o1.getCustomer();//对象导航的方式
System.out.println(c);
tx.commit();
s.close();
}
@Test
public void test15(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Customer c1 = s.get(Customer.class, 1);
Set<Order> orders = c1.getOrders();//对象导航的方式
System.out.println(orders);
tx.commit();
s.close();
}
/*原来JDBC时,留下的思想
@Test
public void test16(){
List<Order> orders = findOrderByCustomerid(1);
System.out.println(orders);
} public List<Order> findOrderByCustomerid(int customerid){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//select * from t_orders where customer_id = ?;
Query query = s.createQuery("from Order o where o.customer.id = ?");
query.setInteger(0, customerid);
List<Order> orders = query.list();
tx.commit();
s.close();
return orders;
}
*/ /*
* 原生SQL查询
* 使用的是session的createSQLQuery方法
* 它的执行list结果是一个由Object数组组成的集合。
*
* 在实际开发中,我们一般不会使用Object数组来操作。
* 而是在执行list()方法之前,调用addEntity方法,给sqlquery添加一个实体,
* 从而得到的就是实体对象的集合 select
sum(
case when score between 0 and 59 then 1 else 0 end
) as E,
sum(
case when score between 60 and 69 then 1 else 0 end
) as D,
sum(
case when score between 70 and 79 then 1 else 0 end
) as C,
sum(
case when score between 80 and 89 then 1 else 0 end
) as B,
sum(
case when score between 90 and 99 then 1 else 0 end
) as A
from t_students */
@Test
public void test14(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
SQLQuery sqlquery = s.createSQLQuery("select * from t_customers");//参数就是SQL语句
//给SQLQuery添加一个实体
sqlquery.addEntity(Customer.class);
List list = sqlquery.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
} }
/*
* 分页查询
* mysql中的分页查询:
* 使用的关键字:limit
* 关键字中的必要条件:firstResult(开始记录索引),maxResults(最大的结果集数)
* sql语句:select * from t_orders limit firstResult,maxResults;
*
* HQL语句:from Order
*
* 在hibernate中分页只有
* setFirstResult
* setMaxResults
* 不管使用什么数据库都是这两个方法
*/
@Test
public void test9(){
List<Order> orders = findAllOrder(4,2);
for(Order o : orders){
System.out.println(o);
}
}
//QBC
public List<Order> findAllOrder2(int firstResult,int maxResults){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Criteria criteria = s.createCriteria(Order.class);
criteria.setFirstResult(firstResult);//开始记录的索引
criteria.setMaxResults(maxResults);//每页要查询的条数
List<Order> list = criteria.list();
tx.commit();
s.close();
return list;
}
//HQL
public List<Order> findAllOrder(int firstResult,int maxResults){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Order");//select * from t_orders
//hibernate提供的方法:
query.setFirstResult(firstResult);//开始记录的索引
query.setMaxResults(maxResults);//每页要查询的条数
List<Order> orders = query.list();
tx.commit();
s.close();
return orders;
}
/*
* 命名查询
* 把sql或者HQL在映射文件中配置起来。提供一个名称。
* 名称和语句之间是键值对的对应关系。
* 在程序使用根据名称获取语句的方法,叫做命名查询
*/
@Test
public void test8(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.getNamedQuery("findCustomerByCondition");
//给条件赋值
query.setString("name", "testD");
query.setInteger("age", 28);
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
/*
* 具名查询
* 具名其实就是给查询的参数占位符提供一个名称,使我们在查询时,使用名称而不是索引来给参数赋值
* 书写规范:
* 必须用 :名称
*/
@Test
public void test7(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer where age > :age and name=:name");
//给条件赋值
query.setString("name", "testD");
query.setInteger("age", 28);
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
//多态查询:了解一下
@Test
public void test6(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from java.io.Serializable ");
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
/*
* 排序查询
* 关键字和SQL是一样的。
* order by
* asc
* desc
*/
@Test
public void test5(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer order by age desc ");
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
/*
* QBC特例查询
* 特例:
* 用实体对象创建一个例子,查询语句的条件就会根据实体对象中提供的例子进行条件的拼装。
* 注意:
* 1、条件只会用等于
* 2、在拼装条件时,它只涉及不为null的字段,同时不会把OID作为条件。
*/
@Test
public void test4(){
//我的例子
Customer c = new Customer();
c.setAge(28);
c.setName("testA");
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//创建查询对象。参数是要查询的实体
Criteria criteria = s.createCriteria(Customer.class);
//添加条件
criteria.add(Example.create(c));//select * from t_customes where age = 28 List list = criteria.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
//QBC带条件查询
@Test
public void test3(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//创建查询对象。参数是要查询的实体
Criteria criteria = s.createCriteria(Customer.class);
//添加条件
criteria.add(Restrictions.gt("age", 28)); List list = criteria.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
/*
* QBC查询
* QBC:query by criteria
* QBC能实现的HQL都能实现。反之亦然。
* 它更加面向对象
*/
@Test
public void test2(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
//创建查询对象。参数是要查询的实体
Criteria criteria = s.createCriteria(Customer.class);
List list = criteria.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
/*
* HQL带条件查询
* hiberante中HQL使用条件,也是where关键字。
* 条件的参数占位符也是用的问号
* 注意事项:
* hibernate中查询语句的参数占位符索引是从0开始的。
*/
@Test
public void test1(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer where age > ?");
//给条件赋值
query.setInteger(0, 28);
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
}
3、多表查询
3.1、交叉连接(cross join)
显示交叉连接:
select t1.*, t2.* from tabel1 t1 cross join table t2
隐式交叉连接:
select t1.*, t2.* from tabel1 t1, table t2
返回结果:两张表的笛卡尔积
3.2、内连接(inner join)
显示内连接:SQL写法
select * from tabel1 t1 inner join table t2 on t1.primary_key = t2.foregin_key
隐式内连接:SQL写法
select * from tabel1 t1, table t2where t1.primary_key = t2.foregin_key
返回结果:是在笛卡尔积的基础上,返回符合条件的结果。
HQL写法:select * from Customer t1 inner join c.orders;
HQL写法:select * from Customer t1 inner join fetch c.orders;
返回结果:
inner join:Object[]
inner join fetch:实体对象
3.3、外连接(outer join 左外和右外其实是一样的)
左外连接: left outer join
select * from customers c left outer join orders o on c.id=o.customer_id;
返回结果:除了满足条件的记录外,左外连接还会返回左表剩余记录。
右外连接: right outer join
select * from customers c right outer join orders o on c.id=o.customer_id;
返回结果:除了满足条件的记录外,右外连接还会返回右表剩余记录。
HQL写法:select * from Customer t1 inner join c.orders;
HQL写法:select * from Customer t1 inner join fetch c.orders;
返回结果:
inner join:Object[]
inner join fetch:实体对象
全外连接:(MySQL不支持) full outer join。返回结果了解一下:除了满足条件的之外,还会返回左表和右表不满足条件的记录。
/*
* 迫切左外连接查询
*
* 返回的是一个集合,集合中的每个元素都是左表实体对象。
* 有可能重复。重复的原因是看迫切左外连接查询的条件。
*/
@Test
public void test11(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer c left outer join fetch c.orders ");
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
} /*
* 左外连接查询
*
* 返回值是一个集合,集合的每个元素是Object数组。
* 数组中包含2个Object对象。
* 其中一个左表对象和一个右表对象。
* 左表对象有可能重复,右表对象不会重复
*/
@Test
public void test10(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer c left outer join c.orders ");
List<Object[]> list = query.list();
tx.commit();
s.close();
for(Object[] os : list){
System.out.println("----------------------");
for(Object o : os){
System.out.println(o);
}
}
}
3.4、投影查询
/*
* 投影查询
* 就想实现
* 查询客户时,只有客户的id和name
* 查询订单时,只有订单id和money
* 投影查询就是:
* 在实际开发中,我们只需要一个实体类中的某个或者某些字段,当查询完成后还想使用该实体对象来操作。
* 需要把查询的某个或某些字段投射到实体类上,这种查询方式叫投影查询。
* 在使用投影查询时的要求:
* 实体类必须提供对应参数列表的构造函数。
* 在写HQL语句时,需要使用new关键字
*
* 注意事项:
* 在使用投影查询时,如果直接写类名,需要确定该类在工程中唯一。
* 如果不唯一的话,需要些类全名。包名.类名
*/
@Test
public void test13(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("select new cn.itcast.domain.Order(o.id,o.money) from Order o");
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
@Test
public void test12(){
Session s = HibernateUtil.getSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("select new Customer(c.id,c.name) from Customer c");
List list = query.list();
tx.commit();
s.close();
for(Object o : list){
System.out.println(o);
}
}
三、附录:QBC自定义条件
短语 | 含义 |
Restrictions.eq | 等于= |
Restrictions.allEq | 使用Map,使用key/value进行多个等于的判断 |
Restrictions.gt | 大于> |
Restrictions.ge | 大于等于>= |
Restrictions.lt | 小于< |
Restrictions.le | 小于等于<= |
Restrictions.between | 对应sql的between子句 |
Restrictions.like | 对应sql的like子句 |
Restrictions.in | 对应sql的in子句 |
Restrictions.and | and 关系 |
Restrictions.or | or关系 |
Restrictions.sqlRestriction | Sql限定查询 |
Restrictions.asc() | 根据传入的字段进行升序排序 |
Restrictions.desc() | 根据传入的字段进行降序排序 |
运算类型 | HQL运算符 | QBC运算方法 |
比较运算 | = | Restrictions.eq() |
<> | Restrictions.not(Restrictions.eq()) | |
>= | Restrictions.ge() | |
< | Restrictions.lt() | |
<= | Restrictions.le() | |
is null | Restrictions.isNull() | |
is not null | Restrictions.isNotNull() | |
范围运算符 | in | Restrictions.in() |
not in | Restrictions.not(Restrictions.in()) | |
between | Restrictions.between() | |
not between | Restrictions.not(Restrictions.between()) |
运算类型 | HQL运算符 | QBC运算方法 |
字符串模式匹配 | like | Restrictions.like() |
逻辑 | and | Restrictions.and()| Restrictions.conjunction() |
or | Restrictions.or()| Restrictions.disjunction() | |
not | Restrictions.not() | |