关于HQL

HQL与SQL非常类似,只不过SQL的操作对象是数据表,列等对象,而HQL操作的是持久化类,实例,属性等。

HQL是完全面向对象的查询语言,因此也具有面向对象的继承,多态等特性。

使用HQL的一般步骤为:

获取session对象

编写HQL语句

使用session的createQuery方法创建查询对象(Query对象)

使用SetXxx(index/para_name, value)为参数复制

使用Query对象的list()方法返回查询结果列表(持久化实体集)

下面演示一下HQL的基本用法,演示之前先附上之前的一个例子,双向N-N关联映射,

假设有下面两个持久化类Person和Event之间成N-N双向关联,代码如下,

Person类

 package hql;

 import java.util.*;

 import javax.persistence.*;

 @Entity
@Table(name = "person_inf")
public class Person
{
@Id @Column(name = "person_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private int age;
@ManyToMany(cascade=CascadeType.ALL, targetEntity=MyEvent.class)
@JoinTable(name = "person_event" ,
joinColumns = @JoinColumn(name = "person_id"
, referencedColumnName="person_id"),
inverseJoinColumns = @JoinColumn(name = "event_id"
, referencedColumnName="event_id")
)
private Set<MyEvent> myEvents
= new HashSet<>();
@ElementCollection(targetClass=String.class)
@CollectionTable(name="person_email_inf",
joinColumns=@JoinColumn(name="person_id" , nullable=false))
@Column(name="email_detail" , nullable=false)
private Set<String> emails
= new HashSet<>(); public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
} public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
} public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
} public void setMyEvents(Set<MyEvent> myEvents)
{
this.myEvents = myEvents;
}
public Set<MyEvent> getMyEvents()
{
return this.myEvents;
} public void setEmails(Set<String> emails)
{
this.emails = emails;
}
public Set<String> getEmails()
{
return this.emails;
}
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
} }

Event类

 package hql;

 import java.util.*;

 import javax.persistence.*;

 @Entity
@Table(name="event_inf")
public class MyEvent
{
@Id @Column(name="event_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private Date happenDate;
@ManyToMany(targetEntity=Person.class , mappedBy="myEvents")
private Set<Person> actors
= new HashSet<>(); public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
} public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return this.title;
} public void setHappenDate(Date happenDate)
{
this.happenDate = happenDate;
}
public Date getHappenDate()
{
return this.happenDate;
} public void setActors(Set<Person> actors)
{
this.actors = actors;
}
public Set<Person> getActors()
{
return this.actors;
}
public MyEvent() {}
public MyEvent(String title, Date happenDate) {
this.title = title;
this.happenDate = happenDate;
}
}

PersonManager类

 package hql;

 import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import java.util.HashSet; public class PersonManager
{ public static void testPerson() throws ParseException
{
Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction();
Person p1 = new Person("张三",20);
p1.getEmails().add("zhangsan@baidu.com");
p1.getEmails().add("zhangsan@google.com"); Person p2 = new Person("李四",30);
p2.getEmails().add("lisi@jd.com"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
MyEvent e1 = new MyEvent("大学毕业", sdf.parse("2012-06-01"));
MyEvent e2 = new MyEvent("参加工作", sdf.parse("2012-10-01"));
MyEvent e3 = new MyEvent("出国旅游", sdf.parse("2013-05-01"));
MyEvent e4 = new MyEvent("回家过年", sdf.parse("2013-12-20"));
MyEvent e5 = new MyEvent("升职加薪", sdf.parse("2014-01-01")); p1.getMyEvents().add(e1);
p1.getMyEvents().add(e3);
p1.getMyEvents().add(e4);
p1.getMyEvents().add(e5); p2.getMyEvents().add(e2);
p2.getMyEvents().add(e3);
p2.getMyEvents().add(e4); sess.save(p1);
sess.save(p2); tx.commit();
sess.close();
} public static void main(String[] args) throws ParseException {
testPerson();
}
}

首先执行上面的PersonManager,我们需要生成数据表如下,

MariaDB [test]> select * from event_inf;
+----------+---------------------+----------+
| event_id | happenDate | title |
+----------+---------------------+----------+
| 1 | 2013-12-20 00:00:00 | 回家过年 |
| 2 | 2013-05-01 00:00:00 | 出国旅游 |
| 3 | 2012-06-01 00:00:00 | 大学毕业 |
| 4 | 2014-01-01 00:00:00 | 升职加薪 |
| 5 | 2012-10-01 00:00:00 | 参加工作 |
+----------+---------------------+----------+
5 rows in set (0.00 sec)

MariaDB [test]> select * from person_event;
+-----------+----------+
| person_id | event_id |
+-----------+----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 2 |
| 2 | 5 |
+-----------+----------+
7 rows in set (0.00 sec)

MariaDB [test]> select * from person_inf;
+-----------+-----+------+
| person_id | age | name |
+-----------+-----+------+
| 1 | 20 | 张三 |
| 2 | 30 | 李四 |
+-----------+-----+------+
2 rows in set (0.00 sec)

MariaDB [test]> select * from person_email_inf;
+-----------+---------------------+
| person_id | email_detail |
+-----------+---------------------+
| 1 | zhangsan@baidu.com |
| 1 | zhangsan@google.com |
| 2 | lisi@jd.com |
+-----------+---------------------+
3 rows in set (0.00 sec)

HQL的基本用法

现在可以写一个查询类用来查询上面的数据,一个最简单的查询是使用session的createQuery方法返回一个Query对象,再用Query对象的list()方法返回结果集,

通常结果集是持久化实体的结果集,可以用类型强制转换还原成原来的持久化类的对象,例如下面这样,

     public static void findPersons() {
Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction(); List pl = sess.createQuery("select distinct p from Person p "
+ "join p.myEvents where title = :eventTitle")
.setString("eventTitle", "出国旅游") //执行setString()为参数赋值
.list(); //Query()调用list()方法获取查询的全部实例
for (Object ele : pl) {
Person p = (Person)ele;
System.out.println(p.getName());
}
tx.commit();
sess.close();
sf.close();
}

上面是最基本的查询方法,HQL语法与SQL非常类似,只不过HQL查询针对的是持久化类,实例及属性,上面查询的是持久化类的实例集合,

在设置参数的时候,可以在HQL中使用冒号(:)后紧接参数名来作为一个参数的占位符,然后在setXXX()为参数赋值,上面代码执行结果如下,

 Hibernate: select distinct person0_.person_id as person_i1_3_, person0_.age as age2_3_, person0_.name as name3_3_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id where title=?
张三
李四

也可以在HQL中使用问号(?)紧接索引的方式设置参数占位符,然后在setXXX()中用按索引为参数赋值,例如下面,

     public static void findPersonsByHappendDate() throws ParseException {
Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
List pl = sess.createQuery("select distinct p from Person p "
+ "inner join p.myEvents event where event.happenDate "
+ "between ?1 and ?2")
.setDate("1", sdf.parse("2012-06-01"))
.setDate("2", new Date())
.list();
for (Object ele : pl) {
Person p = (Person)ele;
System.out.println(p.getName());
}
tx.commit();
sess.close();
sf.close();
}

上面执行结果,

 Hibernate: select distinct person0_.person_id as person_i1_3_, person0_.age as age2_3_, person0_.name as name3_3_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id where myevent2_.happenDate between ? and ?
张三
李四

除了在select中查询持久化类实例之外,也能直接查询属性,例如,

     public static void findPersonProperty() {
Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction(); List pl = sess.createQuery("select distinct p.id, p.name, p.age "
+ "from Person p join p.myEvents")
.list();
for (Object ele : pl) {
Object[] objs = (Object[])ele;
System.out.println(java.util.Arrays.toString(objs));
}
tx.commit();
sess.close();
sf.close();
}

程序运行结果,

 Hibernate: select distinct person0_.person_id as col_0_0_, person0_.name as col_1_0_, person0_.age as col_2_0_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id
[1, 张三, 20]
[2, 李四, 30]

关联和连接

在HQL中最简单的查询语句是from子句(前面不需要select关键字),from后接持久化了类名称(大小写敏感),持久化类可以用有别名,用as关键字(可以省略)。

from后面可以接多个实体类进行表关联,但是实际上这种用法不多,更多的是使用隐式或者显示连接实现跨表连接。

隐式连接

不使用join关键字,而使用点号(.)来隐式连接实体,例如 "from Person p where p.address = xxxx"

但是需要注意的是,在Hibernate 3.2.3之后,隐式连接的使用需要特别注意,当关联的是普通组件时候,可以使用隐式连接,如果关联的是集合属性,就会抛出illegal attempt to dereference collection...异常

显式连接

HQL中支持以下显示连接,分别与SQL99中的各种连接对应

inner join, 可简写成join

left outer join, 可简写成left join

right outer join, 可简写成right join

full join

下面是一个显式连接的例子,通过打印出来的SQL语句会发现,HQL会自动根据持久化类之间的关联关系,生成对应的连接表的 with (等同于SQL语句里join的on关键字)条件,即使没有在HQL中显式地写出with条件。

HQL:

 "select p from Person p inner join p.emails e where e = :email"

SQL:

 Hibernate:
select
person0_.person_id as person_i1_3_,
person0_.age as age2_3_,
person0_.name as name3_3_
from
person_inf person0_
inner join
person_email_inf emails1_
on person0_.person_id=emails1_.person_id
where
emails1_.email_detail=?

查询结果集

延迟加载

Hibernate默认开启了延迟加载,如果session关闭,则无法继续通过实体对象获取数据。

例如Person关联的属性emails,默认加载Person时候并不会去获取emails属性值,一旦session关闭就无法获取emails了,

为了解决这个问题,可以在HQL中使用join fetch关键字,例如下面的例子,

     public static void joinFetch() {
Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction(); List pl = sess.createQuery("from Person p join fetch p.myEvents")
.list();
tx.commit();
sess.close();
sf.close(); for(Object ele : pl) {
Person p = (Person)ele;
System.out.println(p.getMyEvents().iterator().next().getTitle());
}
}

我们将读取数据放在session关闭之后,发现依然可以后去myEvents属性,

 Hibernate:
select
person0_.person_id as person_i1_3_0_,
myevent2_.event_id as event_id1_0_1_,
person0_.age as age2_3_0_,
person0_.name as name3_3_0_,
myevent2_.happenDate as happenDa2_0_1_,
myevent2_.title as title3_0_1_,
myevents1_.person_id as person_i1_3_0__,
myevents1_.event_id as event_id2_2_0__
from
person_inf person0_
inner join
person_event myevents1_
on person0_.person_id=myevents1_.person_id
inner join
event_inf myevent2_
on myevents1_.event_id=myevent2_.event_id
出国旅游
出国旅游
出国旅游
出国旅游
参加工作
参加工作
参加工作

Select 子句

select子句接单个持久化类

如果select后面只查询单个持久化类,那么返回的查询结果是一个集合,每一个集合元素可以直接通过强制类型转换还原成原来的数据类型,例如

         List list0 = sess
.createQuery("select p from Person p").list();

首先可以用for(Object ele : list0)遍历结果集,对每一个元素可以直接强制转换成原来的数据类型,

         for (Object ele : list0) {
Person p = (Person)ele;
System.out.println(p.getName());
}

select子句接持久化类和属性混合

select子句可以接持久化类,或者其属性,通常select子句查询的结果就是一个集合,集合的每个元素都是数组,相当于返回的是一个二维数组结果集,

         List pl = sess
.createQuery(
"select p.name, e from Person p join p.myEvents e "
+ "where e.title = :title")
.setString("title", "回家过年").list();

因此需要先将每个集合元素还原成数组,再次将数组每一项强制换换成对应数据类型,

         for (Object ele : pl) {
Object[] objs = (Object[]) ele;
String name = (String) objs[0];
MyEvent e = (MyEvent) objs[1];
System.out.println(name + "," + e.getTitle());
}

首先上面sess.createQuery(xxx).list()返回的是一个集合,因此用for(Object ele : pl)遍历每一个元素,每一个元素又是一个数组,因此用Object[] objs = (Object[])ele; 强制转换去还原集合每一个元素,而对于数组每一项的数据类型,则是根据HQL中select后面的顺序,逐一匹配,因此在for循环里用来String和MyEvent来还原数组每一项。

这是select最通用的用法。

select 子句直接生成list对象或者map对象

Query对象返回的集合中,每一个元素就是一个list对象,每个list对象,例如像下面这样,

         List list1 = sess
.createQuery(
"select new list(p.name, p.age, e) from Person p left join p.emails e")
.list();

返回的集合中,每一个元素就是list对象,用强制类型转换还原即可,遍历每一个list对象,就是select后面list中的每个元素,

         for (Object ele : list1) {
List name = (List) ele;
Iterator it = name.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}

当然也可以在select子句之后直接生成map对象,使用别名p.name as pname作为map的key,其实际值作为value,

相当于Query对象通过.list()方法返回的结果集中,包含了n个map对象,每个map对象里都只有一个key-value对,每个key名字都交pname,

         List list2 = sess
.createQuery(
"select new map(p.name as pname) from Person p")
.list();

可以像下面这样遍历map,

         for (Object ele : list2) {
Map mName = (HashMap) ele;
Set keySet = (Set)mName.keySet();
Iterator it = keySet.iterator();
while( it.hasNext()) {
Object key = it.next();
System.out.println(key+"->"+mName.get(key));
}
}

输出结果,

 pname->张三
pname->李四

select子句甚至可以直接跟持久化类的构造函数

         List list3 = sess
.createQuery(
"select new MyEvent(e.title, e.happenDate) from Person p left join p.myEvents e")
.list();

这样查询出的效果跟直接查询一个持久化类一样,只不过这里是用select的结果去初始化一个持久化类了,依然可以遍历每一个集合元素,直接强制转换成对应持久化类实例,

         for (Object ele : list3) {
MyEvent e = (MyEvent)ele;
System.out.println(e.getTitle());
}

多态查询

HQL支持多态,from后跟持久化类名,不仅会查出持久化类的全部实例,还会查出该类的子类的全部实例。

即,当我们用HQL查询父类或者接口时,父类的子类,或者接口的实现类的实例都会一起被查询出来。

注意,一张数据表代表一个持久化类,表中一行就带表一个持久化类的实例。

所以按照多态查询的规则,如果在我们前面的测试工程中,查询 java.lang.Object类会有什么结果呢,例如

 List list4 = sess.createQuery("from java.lang.Object o").list();

分析上面的HQL,结合HQL的多态性质, 我们知道Object是所有java类的父类,在本工程中,Person和MyEvent都是Object的子类,所以这两个类的所有实例都将被查询出来,

Person_inf表中有两条记录,event_inf表中有5条记录,所以最终会总共会查询出7个实例,我们直接将每个实例的内存映射打印出来,

         for(Object ele : list4) {
System.out.println(ele);
}

首先我们会看到Hibernate生成了两条SQL语句,分别用来查询person_inf和event_inf表,这个可以理解的,因为本工程中Object有两个子持久化类。

 Hibernate:
select
person0_.person_id as person_i1_3_,
person0_.age as age2_3_,
person0_.name as name3_3_
from
person_inf person0_
Hibernate:
select
myevent0_.event_id as event_id1_0_,
myevent0_.happenDate as happenDa2_0_,
myevent0_.title as title3_0_
from
event_inf myevent0_

打印的实例的内存映射如下,

 hql.Person@350c420a
hql.Person@e6c75827
hql.MyEvent@75ae7c20
hql.MyEvent@62ee0677
hql.MyEvent@214e7ddc
hql.MyEvent@cfad90e0
hql.MyEvent@226c3fff

可以看到内存映射中,也是按持久化类的顺序排列的,我们甚至可以将每一个实例的内存映射进行强制类型转换,还原成真正的持久化类的对象,比如下面这样,

         Person p1 = (Person)list4.get(0);
Person p2 = (Person)list4.get(1);
System.out.println(p1.getName()+","+p2.getName()); MyEvent e1 = (MyEvent)list4.get(2);
MyEvent e2 = (MyEvent)list4.get(3);
MyEvent e3 = (MyEvent)list4.get(4);
MyEvent e4 = (MyEvent)list4.get(5);
MyEvent e5 = (MyEvent)list4.get(6);
System.out.println(e1.getTitle()+","+e2.getTitle()+","+e3.getTitle()+","
+ ""+e4.getTitle()+","+e5.getTitle());

现在就可以直接用对象去访问属性了,上面程序片段输出结果为,

 张三,李四
出国旅游,升职加薪,大学毕业,回家过年,参加工作

可见刚好将每张表的所有记录(即实例)打印出来了!

where子句

引用关联属性的隐式连接和显示连接

where子句后面可以可以使用属性来限定范围,属性可以是普通属性或者组件属性,但是需要特别注意集合属性。

在3.2.3以后的版本中,如果属性为集合属性,那么不能直接在where子句后面使用点号(.)来访问,因为这在底层会转换成多表连接查询,即隐式连接。 3.2.3之后的版本是不支持集合属性的隐式连接的,需要显示join连接。

例如下面这个查询,Person的myEvents是一个集合属性,p.myEvents对应的是一个关联的MyEvent实体,在底层会隐式连接person_inf和event_inf表。

 List list0 = sess.createQuery("select p from Person p where p.myEvents.title is not null").list();

抛出异常,

 Exception in thread "main" org.hibernate.QueryException: illegal attempt to dereference collection [person0_.person_id.myEvents] with element property reference [title] [select p from hql.Person p where p.myEvents.title is not null]
at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:93)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:167)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1836)
at hql.HqlQuery.testWhere(HqlQuery.java:245)
at hql.HqlQuery.main(HqlQuery.java:265)

这将引发集合属性隐式连接异常,抛出 illegal attempt to dereference collection 的错误,

必须将上面的查询改为显示连接,

         List list0 = sess.createQuery("select p from Person p inner join p.myEvents e "
+"where e.title is not null").list();

用特殊关键字【id】引用任何主键

不论持久化类中的标识属性(表关键字)定义成什么名字,在HQL中都可以用关键字id来代替,例如

在MyEvent持久化类中,定义了eventId为标识属性,

 @Entity
@Table(name="event_inf")
public class MyEvent
{
@Id @Column(name="event_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer eventId;
...

在HQL中,使用id引用持久化类的标识属性,

 List list1 = sess.createQuery("from MyEvent e where e.id = 1").list();

子查询

HQL中支持在select和where后面进行子查询,例如这样,

         List list1 = sess.createQuery("select (select id from MyEvent e where id=1) from MyEvent "
+ "where id = (select id from Person p where id = 2)").list();

上面代码将生成以下SQL语句,可以看到HQL子查询与SQL子查询基本一致。

 Hibernate:
select
(select
myevent1_.event_id
from
event_inf myevent1_
where
myevent1_.event_id=1) as col_0_0_
from
event_inf myevent0_
where
myevent0_.event_id=(
select
person2_.person_id
from
person_inf person2_
where
person2_.person_id=2
)

命名查询(注解查询)

Hibernate提供了一个@NamedQuery注解可以将原本写在createQuery()中的HQL放在注解中,之后通过sess.getNamedQuery()取出注解上的配置进行查询,一样会返回Query对象,后续跟普通查询流程一样,如下面的例子,

在Person实体类上我们增加一个命名查询注解

 @Entity
@Table(name = "person_inf")
@NamedQuery(name="myNamedQuery", query="select p from Person as p where p.age > ?")
public class Person
{
...

调用方法如下,

         List list1 = sess.getNamedQuery("myNamedQuery")
.setInteger(0, 25)
.list();
for(Object ele : list1) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

命名查询的本质只是将java代码中的HQL放在注解中去配置了。

条件查询

条件查询需要使用sess.createCriteria(Class)来返回Criteria对象,一个Criteria对象就代表一次查询,通过Criteria对象的add方法可以添加查询条件, 查询条件通过工具类Restrictions中的方法来指定,例如下面这样,

         List list1 = sess.createCriteria(Person.class)
.add( Restrictions.gt("age", 25))
.list();

其中工具类Restrictions支持很多静态方法,用来做查询条件,例如 gt代表“大于”,lt代表“小于”等等。

之后就能得到查询结果集,和之前的处理方法一样。

         for(Object ele : list1) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

关联属性实体的条件查询

如果要在关联属性的实体上增加查询条件,就需要对关联属性再次使用 createCritieria()方法,例如要在Person的关联属性myEvents上增加条件查询,

         List list2 = sess.createCriteria(Person.class)
.add( Restrictions.gt("age", 25))
.createCriteria("myEvents", JoinType.LEFT_OUTER_JOIN)
.add( Restrictions.isNotNull("title"))
.list();
for(Object ele : list2) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

createAlias()

createAlias()也可以实现在关联属性上增加条件查询,与createCritieria()不同的是,createAlias()仅仅是给关联实体起一个别名,让后面的过滤条件可以根据该关联实体的别名进行筛选,而不是创建一个新的Criteria实例。

         List list3 = sess.createCriteria(Person.class)
.add( Restrictions.gt("age", 25))
.createAlias("myEvents", "eve")
.add( Restrictions.isNotNull("eve.title"))
.list();
for(Object ele : list3) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

条件查询上的延迟加载

与HQL中的fetch关键字一样,Critieria实例也可以增加延迟加载配置,使用setFetchMode()即可,有三个可选值,

DEFAULT:使用配置文件指定延迟加载策略

JOIN:使用外连接、预初始化关联实体.(即不使用延迟加载)

SELECT:启用延迟加载,系统将使用单独的select语句来初始化关联实体,之后真正要访问关联实体的时候,才会执行第二天条select语句。

下面是一个启用延迟加载的例子,

         List list4 = sess.createCriteria(Person.class)
.add( Restrictions.gt("age", 25))
.setFetchMode("myEvents", FetchMode.SELECT)
.list();
tx.commit();
sess.close();
sf.close(); for(Object ele : list4) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
Set<MyEvent> e = p.getMyEvents();
Iterator it = e.iterator();
while (it.hasNext()) {
MyEvent ee = (MyEvent)it.next();
System.out.println(ee.getTitle());
}
}

我们在第3行启用延迟加载,那么一旦session关闭之后,关联实体就不能访问了,在第12行会抛出 failed to lazily initialize a collection of role 的异常,

查看Hibernate生成的SQL,会发现只查询了person_inf表,

 Hibernate:
select
this_.person_id as person_i1_3_0_,
this_.age as age2_3_0_,
this_.name as name3_3_0_
from
person_inf this_
where
this_.age>?
李四,30
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hql.Person.myEvents, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:215)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:555)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:143)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
at hql.HqlQuery.testCriteria(HqlQuery.java:353)
at hql.HqlQuery.main(HqlQuery.java:372)

如果我们将上面代码第3行改成.setFetchMode("myEvents", FetchMode.JOIN),即关闭延迟加载,那么关联属性就会立即查询出来,可以看到生成的SQL使用left out join进行了连接查询。

 Hibernate:
select
this_.person_id as person_i1_3_1_,
this_.age as age2_3_1_,
this_.name as name3_3_1_,
myevents2_.person_id as person_i1_3_3_,
myevent3_.event_id as event_id2_2_3_,
myevent3_.event_id as event_id1_0_0_,
myevent3_.happenDate as happenDa2_0_0_,
myevent3_.title as title3_0_0_
from
person_inf this_
left outer join
person_event myevents2_
on this_.person_id=myevents2_.person_id
left outer join
event_inf myevent3_
on myevents2_.event_id=myevent3_.event_id
where
this_.age>?
李四,30
回家过年
出国旅游
参加工作
李四,30
回家过年
出国旅游
参加工作
李四,30
回家过年
出国旅游
参加工作

投影,聚合,分组

Hibernate的条件查询中的所谓的投影运算就是按列查询,具体分成两种,一种是根据列来进行统计,使用Projection接口实现,类似于SQL中的聚集函数(count,AVG,groupby 等)

另一种就是直接按列查询,Hibernate的条件查询中使用Property()方法,其作用类似SQL中的select

Projection投影运算

所谓HQL中的投影,聚合,分组,其实就是SQL中的一些聚合函数,例如统计记录条数count(), 计算平均值avg(),统计最大值max(),以及分组统计group by等等。

在条件查询中可以通过Projection接口实现这些功能。工具类Projections提供了很多静态方法来实现上面的功能,下面是基本用法,

         List list1 = sess.createCriteria(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Projections.projectionList()
.add(Projections.rowCount())
.add(Projections.max("eve.title"))
.add(Projections.groupProperty("eve.title")))
.list();
for(Object ele : list1) {
Object[] objs = (Object[])ele;
for (Object obj : objs) {
System.out.print(obj+",");
}
System.out.println("\n==========");
}

看看hibernate生成的SQL就能知道Projection的功能了,

 Hibernate:
select
count(*) as y0_,
max(eve1_.title) as y1_,
eve1_.title as y2_
from
person_inf this_
inner join
person_event myevents3_
on this_.person_id=myevents3_.person_id
inner join
event_inf eve1_
on myevents3_.event_id=eve1_.event_id
group by
eve1_.title

其实就是在SQL中使用了一些聚集函数统计而已,统计结果如下,

 2,出国旅游,出国旅游,
==========
1,升职加薪,升职加薪,
==========
1,参加工作,参加工作,
==========
2,回家过年,回家过年,
==========
1,大学毕业,大学毕业,
==========

指定别名及排序

可以为Projection方式的聚集统计结果起一个别名,用来根据统计结果排序等。通常会有三种方式起别名。

第一种是使用Projections工具的Alias()方法

         List list2 = sess.createCriteria(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Projections.projectionList()
.add(Projections.alias(Projections.rowCount(), "c"))
.add(Projections.max("eve.title"))
.add(Projections.groupProperty("eve.title")))
.addOrder(Order.asc("c"))
.list();
for(Object ele : list2) {
Object[] objs = (Object[])ele;
for (Object obj : objs) {
System.out.print(obj+",");
}
System.out.println("\n==========");
}

上面是按照记录条数排序,结果如下,

 1,升职加薪,升职加薪,
==========
1,大学毕业,大学毕业,
==========
1,参加工作,参加工作,
==========
2,出国旅游,出国旅游,
==========
2,回家过年,回家过年,
==========

第二种方法是使用SimpleProject的as()方法指定别名

这种方法要求Projections后的聚集函数必须是SimpleProject类或者子类,

         List list3 = sess.createCriteria(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Projections.projectionList()
.add(Projections.rowCount())
.add(Projections.max("eve.title"))
.add(Projections.groupProperty("eve.title").as("c")))
.addOrder(Order.asc("c"))
.list();
for(Object ele : list3) {
Object[] objs = (Object[])ele;
for (Object obj : objs) {
System.out.print(obj+",");
}
System.out.println("\n==========");
}

上面是按照title排序,结果如下,

 2,出国旅游,出国旅游,
==========
1,升职加薪,升职加薪,
==========
1,参加工作,参加工作,
==========
2,回家过年,回家过年,
==========
1,大学毕业,大学毕业,
==========

第三种是使用ProjectList的重载方法add()时指定别名

         List list4 = sess.createCriteria(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Projections.projectionList()
.add(Projections.rowCount(),"c")
.add(Projections.max("eve.title"))
.add(Projections.groupProperty("eve.title")))
.addOrder(Order.asc("c"))
.list();
for(Object ele : list4) {
Object[] objs = (Object[])ele;
for (Object obj : objs) {
System.out.print(obj+",");
}
System.out.println("\n==========");
}

上面也是按照记录条数排序

Property投影运算

除了Projections工具类之外,Property方法也可以进行投影运算,但Projections主要是用来进行统计计算,而Property则主要是用来选择指定的类,作用类似SQL中的select.

基本用法如下,

         List list6 = sess.createCriteria(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Projections.projectionList()
.add(Property.forName("name"))
.add(Property.forName("eve.title")))
.add(Property.forName("eve.title").eq("回家过年"))
.list();
for(Object ele : list6) {
Object[] objs = (Object[])ele;
for (Object obj : objs) {
System.out.print(obj+",");
}
System.out.println("\n==========");
}

上面代码的意思是,先选出Person类的name属性和MyEvent类的title属性(当然事先需要对这两个持久化类进行关联查询),然后按照title过滤,只选出title为“回家过年”的记录,

Hibernate生成的SQL如下,

     select
this_.name as y0_,
eve1_.title as y1_
from
person_inf this_
inner join
person_event myevents3_
on this_.person_id=myevents3_.person_id
inner join
event_inf eve1_
on myevents3_.event_id=eve1_.event_id
where
eve1_.title=?

查询结果如下,

 张三,回家过年,
==========
李四,回家过年,
==========

DetachedCriteria离线查询和子查询

这里所谓的离线查询,就是在session打开之前定义好查询语句,获取一个DetachedCriteria的实例,这样可以在任意的session中使用这个查询对象,

例如这样,

         DetachedCriteria query = DetachedCriteria
.forClass(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Property.forName("name"));

之后在任意的session中,使用DetachedCriteria类的getExecutableCriteria()可以调用这个查询实例进行离线查询,

例如这样,

 List list1 = query.getExecutableCriteria(sess).list();

另外,如果在session使用了条件查询,在条件查询中又使用了Projection的eq(), eqAll(), gt(), in()...等一系列类似运算符的方法,这时可以将session外面定义的DetachedCriteria放在eq(), eqAll(), gt(), in()中作为一个子查询,

例如这样,

         List list2 = sess.createCriteria(Person.class)
.add( Property.forName("name").in(query))
.list();

也就是说,当直接使用DetachedCriteria对象的getExecutableCriteria()进行查询的时候,DetachedCriteria对象就是一个离线查询,

当在session中使用DetachedCriteria查询的时候,就是一个子查询,

下面是一个完整的离线查询和子查询的例子,

     public static void testDetachedCriteria() {
DetachedCriteria query = DetachedCriteria
.forClass(Person.class)
.createAlias("myEvents", "eve")
.setProjection(Property.forName("name")); Configuration conf = new Configuration().configure();
conf.addAnnotatedClass(Person.class);
conf.addAnnotatedClass(MyEvent.class);
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction(); List list1 = query.getExecutableCriteria(sess).list();
15 System.out.println(list1); List list2 = sess.createCriteria(Person.class)
.add( Property.forName("name").in(query))
.list();
System.out.println(list2);
tx.commit();
sess.close();
sf.close();
}

对于上面第14行的离线查询,将会看到下面这样的SQL

 Hibernate:
select
this_.name as y0_
from
person_inf this_
inner join
person_event myevents3_
on this_.person_id=myevents3_.person_id
inner join
event_inf eve1_
on myevents3_.event_id=eve1_.event_id

对于上面第17行的子查询,将会看到下面这样的SQL

 Hibernate:
select
this_.person_id as person_i1_3_0_,
this_.age as age2_3_0_,
this_.name as name3_3_0_
from
person_inf this_
where
this_.name in (
select
this_.name as y0_
from
person_inf this_
inner join
person_event myevents3_
on this_.person_id=myevents3_.person_id
inner join
event_inf eve1_
on myevents3_.event_id=eve1_.event_id
)

原生SQL查询

Hibernate还支持原生的SQL查询,但是通常不建议在新项目中这么做,而是用在老系统上。使用session的createSQLQuery()方法可以用原生SQL进行查询并得到Query对象,后续用法与之前一样。

标量查询

即直接查出值(而不是实体类对象),因为是直接查出的值,需要使用Query对象的addScalar()筛选指定的列,并为其制定数据类型

比如这样,

         String sqlString = "select p.* from person_inf p";
List list1 = sess.createSQLQuery(sqlString)
.addScalar("name", StandardBasicTypes.STRING)
.addScalar("age",StandardBasicTypes.INTEGER)
.list();
for(Object ele : list1) {
Object[] row = (Object[])ele;
System.out.println(row[0]+","+row[1]);
}

实体查询

也可以将SQL查出的结果转换成实体类,前提条件是必须查出所有列才行。使用Query对象的addEntity()方法可以将查询结果集转换成实体类,

比如这样,

         List list2 = sess.createSQLQuery(sqlString)
.addEntity(Person.class)
.list();
for(Object ele : list2) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

涉及到多表查询的时候,可以同时转换多个实体类,比如这样,

         String sqlString3 = "select p.*,pe.*,e.* from person_inf p, person_event pe, event_inf e "
+ "where p.person_id = pe.person_id "
+ "and e.event_id = pe.event_id";
List list3 = sess.createSQLQuery(sqlString3)
.addEntity("p",Person.class)
.addEntity("e", MyEvent.class)
.list();

转为普通javabean

可以使用Query对象的setResultTransformer()方法,

         String sqlString4 = "select p.name, p.age from person_inf p";
List list4 = sess.createSQLQuery(sqlString4)
.setResultTransformer(Transformers.aliasToBean(Student.class))
.list();

关联实体类处理

使用Query对象的addJoin()方法可以将原生SQL中的部分查询结果转换为关联的实体类,例如下面,

         String sqlString5 = "select * from person_inf p, person_event pe, event_inf e "
+ "where p.person_id = pe.person_id "
+ "and e.event_id = pe.event_id";
List list5 = sess.createSQLQuery(sqlString5)
.addEntity("p", Person.class)
.addJoin("e", "p.myEvents")
.list();

对比上面的多表查询转换为实体类的例子,发现与这里的关联实体类处理结果是一样的。

命名SQL

可以使用配置文件或者注解,将SQL从源码中拿出来单独管理,以实现程序解耦。

单一实体类SQL查询

Hibernate中使用@NamedNativeQuery注解来定义命名SQL查询,通常其结构如下,

   @NamedNativeQuery(name="simpleQuery"
, query="select e.event_id as person_id, e.title as name, 1 as age from event_inf e"
, resultClass=Person.class)

可以看到命名SQL查询的注解中,通常有三个参数,其中resultClass这个参数可以指定查出的结果将要转换成哪个实体类的实例。

在程序中,将会这样调用SQL命名查询,

 List list6 = sess.getNamedQuery("simpleQuery").list();

然后在session关闭之后,依然可以遍历list中的数据,我们在前面指定了此命名SQL查询将返回Person类的实例,所以下面直接将结果转换,

         for (Object ele : list6) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

多个实体类、结果集查询

命名SQL不仅可以返回实体类,同时还可以返回结果集,最终的返回结果将是包含实体类和普通结果集的混合体,

这种情况下, resultClass属性已经无法满足了,我们需要使用resultSetMapping来指定将会返回什么,而resultMapping属性又依赖于@SqlResultSetMapping注解的定义。

下面是一个会返回多个实体类,结果集混合查询注解的例子,首先需要定义命名SQL查询注解,

 @NamedNativeQuery(name = "queryTest"
, query = "select p.*,e.*,p.name from person_inf p, person_event pe, event_inf e "
+ "where p.person_id = pe.person_id " + "and e.event_id = pe.event_id"
, resultSetMapping = "firstMapping")

我们指定返回方式为resultSetMapping, 并指定代称为firstMapping,下面需要配置@SqlResultSetMapping来指定返回的结果集,

 @SqlResultSetMapping(name = "firstMapping"
, entities = {
@EntityResult(entityClass = Person.class),
@EntityResult(entityClass = MyEvent.class, fields = {
@FieldResult(name="eventId", column="e.event_id"),
@FieldResult(name = "title", column = "e.title"),
@FieldResult(name = "happenDate", column = "e.happenDate"), }) }
, columns = { @ColumnResult(name = "p.name", type = String.class) })

即,在Query的结果集的每一行中,我们需要先返回Person实体类实例,再返回MyEvent实体类实例,还要返回一个字符串,那么对应读取结果集的代码如下,

         for(Object ele : list7) {
Object[] ents = (Object[])ele;
Person p = (Person)ents[0];
MyEvent e = (MyEvent)ents[1];
String name = (String)ents[2];
System.out.println(p.getName()+","+e.getTitle()+","+name);
}

调用存储过程

Hibernate是通过命名SQL查询来调用存储过程的,只需将存储过程函数放入命名SQL注解的query参数中。

下面我们在mysql中定义一个存储过程如下,

 create PROCEDURE select_all_event()
select e.event_id as person_id, e.title as name, 1 as age from event_inf e

然后将存储过程函数名放入命名SQL注解的query参数中,

 @Entity
@Table(name="event_inf")
@NamedNativeQuery(name="simpleQuery"
, query="{call select_all_event()}"
, resultClass=Person.class)
public class MyEvent
{

之后在代码中的调用方法与普通的执行命名SQL的方法没有区别,

         List list1 = sess.getNamedQuery("myNamedQuery")
.setInteger(0, 25)
.list();
for(Object ele : list1) {
Person p = (Person)ele;
System.out.println(p.getName()+","+p.getAge());
}

使用定制SQL

当我们持久化一个实体的时候(调用session.save()或session.persist()),Hibernate会自动为我们生产成SQL语句,

但是如果我们不想使用Hibernate自动生成的SQL,而是希望自定义SQL呢。

那么我们可以使用Hibernate的定制SQL的注解:@SQLInsert, @SQLUpdate,@SQLDelete,@SQLDeleteAll等等,例如下面这样,

 @SQLInsert(sql="insert into student_inf(student_id,age,name) values(100,?,?)")
@SQLUpdate(sql="update student_inf set name=?,age=? were person_id=?")
@SQLDelete(sql="delete from student_inf where person_id=?")
@SQLDeleteAll(sql="delete from student_inf")
@Entity
@Table(name="student_inf")
public class Student {
@Id @Column(name = "student_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private int age;

我们定制了4个SQL语句,其中在insert语句中,我们故意让student_id=100,

在持久化的代码中没有任何特殊,

         Student s = new Student("abc",22);
sess.save(s);

在不定制SQL的情况下,Hibernate将为我们生成 Hibernate: insert into student_inf (age, name) values (?, ?) 这样的SQL语句插入数据,

而在定制SQL的情况下,我们发现Hibernate使用的是我们定制的SQL语句来插入数据,

 Hibernate: insert into student_inf(student_id,age,name) values(100,?,?)

查看mysql数据库,发现确实插入了一条id为100的记录,

 MariaDB [test]> select * from student_inf;
+------------+-----+------+
| student_id | age | name |
+------------+-----+------+
| 1 | 22 | abc |
| 100 | 22 | abc |
+------------+-----+------+
2 rows in set (0.00 sec)

数据过滤

在Hibernate中可以使用@Filter进行数据过滤,其实就是将HQL中的where条件提取到注解中,有点类似于命名SQL一样。

使用@Filter 之前需要先使用@FilterDef定义过滤器,将过滤器应用到具体实体类或者关联属性之后,还需要在session中开启过滤器。下面是一个例子,

首先在Person实体类上定义一个过滤器,

 //定义一个过滤器
@FilterDef(name="eDate"
,parameters={@ParamDef(name="eff_start_date", type="date"),@ParamDef(name="eff_end_date", type="date")})
@Entity
@Table(name = "person_inf")
public class Person
{

接着我们在一个关联属性上使用这个过滤器,

     @ManyToMany(cascade=CascadeType.ALL, targetEntity=MyEvent.class)
@JoinTable(name = "person_event" ,
joinColumns = @JoinColumn(name = "person_id"
, referencedColumnName="person_id"),
inverseJoinColumns = @JoinColumn(name = "event_id"
, referencedColumnName="event_id")
)
@Filter(name="eDate"
, condition="happenDate BETWEEN :eff_start_date and :eff_end_date")
private Set<MyEvent> myEvents
= new HashSet<>();

之后,我们还需要在session中开启过滤器,后续装载实体类时,就会自动应用这个过滤器,

         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sess.enableFilter("eDate")
.setParameter("eff_start_date", sdf.parse("2013-06-01"))
.setParameter("eff_end_date", sdf.parse("2015-06-01"));

List list1 = sess.createQuery("select distinct e from Person p join p.myEvents e").list();
for( Object ele : list1 ) {
MyEvent e = (MyEvent)ele;
System.out.println(e.getTitle()+","+e.getHappenDate());
}

我们看到,Hibernate生成的SQL,自动添加了过滤条件,

 Hibernate:
select
distinct myevent2_.event_id as event_id1_0_,
myevent2_.happenDate as happenDa2_0_,
myevent2_.title as title3_0_
from
person_inf person0_
inner join
person_event myevents1_
on person0_.person_id=myevents1_.person_id
inner join
event_inf myevent2_
on myevents1_.event_id=myevent2_.event_id
and myevent2_.happenDate BETWEEN ? and ?
 回家过年,2013-12-20 00:00:00.0
升职加薪,2014-01-01 00:00:00.0

Hibernate之HQL基本用法的更多相关文章

  1. JavaWeb_(Hibernate框架)Hibernate中数据查询语句HQL基本用法

    HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式. ...

  2. Hibernate学习-Hibernate查询语言HQL

    HQL(Hibernate Query Language)Hibernate查询语言,语法类似于SQL,可以直接使用实体类及属性. 使用HQL 可以避免使用JDBC 查询的一些弊端 不需要再编写繁复的 ...

  3. Hibernate五 HQL查询

    HQL查询一 介绍1.HQL:Hibernate Query Language,是一种完全面向对象的查询语言.使用Hibernate有多重查询方式可供选择:hibernate的HQL查询,也可以使用条 ...

  4. Hibernate学习---QBC_hibernate完整用法

    QBC(Query By Criteria) API提供了检索对象的另一种方式,它主要有Criteria接口.Criterion接口和Expresson类组成,它支持在运行时动态生成查询语句. Hib ...

  5. hibernate的hql查询

    1.概念介绍 1.Query是Hibernate的查询接口,用于从数据存储源查询对象及控制执行查询的过程,Query包装了一个HQL查询语句. 2.HQL是Hibernate Query Langua ...

  6. Hibernate之HQL查询

    一.Hibernate 提供了以下几种检索对象的方式: 导航对象图检索方式: 根据已经加载的对象导航到其他对象 OID 检索方式: 按照对象的 OID 来检索对象 HQL 检索方式:使用面向对象的 H ...

  7. Hibernate之HQL介绍

    Hibernate中提供了多种检索对象的方式,主要包括以下种类: 导航对象图检索方式:根据已经加载的对象导航到其他对象 OID检索方式:根据对象的OID来检索对象 HQL检索方式:使用面向对象的HQL ...

  8. Hibernate 的hql查询简介【申明:来源于网络】

    Hibernate 的hql查询简介[申明:来源于网络] Hibernate 的hql查询简介:http://blog.csdn.net/leaf_130/article/details/539329 ...

  9. Hibernate的hql语句save,update方法不执行

    Hibernate的hql语句save,update方法不执行 可能出现的原因问题: 未进行事务管理 需要进行xml事务配置或者注解方式的事务配置

随机推荐

  1. LeetCode: Word Ladder [126]

    [题目] Given two words (start and end), and a dictionary, find the length of shortest transformation s ...

  2. Web前端开发实战6:CSS实现导航菜单结合二级下拉式菜单的简单变换

    前面几篇博文都在讲导航菜单和二级下拉式菜单,事实上有非常多方法都能够实现的.详细的情况还要视情况而定. 在后面学习到jQuery框架之后,会有更丰富的动画效果.因为在学习Ajax和jQuery的初步阶 ...

  3. C++数组类模板

    * 作为数组类模板,肯定没有vector做得好,可是普通的数组有1个优点就是能直接操作内存.vector在这方面就不是非常方便了. 网上尽管也有数组类模板.多维的设计基本上都不是非常好.我这个类模板多 ...

  4. oc08--局部变量,全局变量,函数方法的区别

    // // main.m // 局部变量和全局变量以及成员变量的区别 #import <Foundation/Foundation.h> @interface Person : NSObj ...

  5. 【BZOJ3218】【UOJ#77】a + b Problem

    题目 题目在这里 思路&做法 明显的最小割(其实是之前做过一道类似的题) S向每一个格子连容量为\(b_i\)的边 每一个格子向T连容量为\(w_i\)的边 对于格子\(i\)向满足条件的格子 ...

  6. 移动端的click事件延迟触发的原理是什么?如何解决这个问题?

    移动端的click事件延迟触发的原理是什么?如何解决这个问题? 原理 :移动端屏幕双击会缩放页面 300ms延迟 会出现点透现象 在列表页面上创建一个弹出层,弹出层有个关闭的按钮,你点了这个按钮关闭弹 ...

  7. css要点

    1.对inline-block设置overflow: hidden会造成baseline移动,因此需要设置vertical-align才不会出现样式问题. 2.使用flex时,需要对设置flex: 1 ...

  8. vue-cli3+typescript+router

    vue基于类的写法,和基于对象的写法并不一致.使用vue-cli3创建的项目,src目录下的文件结构并没有多大区别,store.router.app.view.components.aeests该有的 ...

  9. 推荐10个超棒的jQuery工具 提示插件

    脚本之家 http://www.jb51.net/article/28525.htm

  10. ML二:NNSearch数据结构--二叉树

    wiki百科:http://zh.wikipedia.org/wiki/%E5%86%B3%E7%AD%96%E6%A0%91%E5%AD%A6%E4%B9%A0 opencv学习笔记--二杈决策树: ...