一、Hql 入门
1、实体类:
package learn.hibernate.bean; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * 持久化类设计 * 注意: * 持久化类通常建议要有一个持久化标识符(ID) * 持久化标识符通常建议使用封装类(例如:Integer 因为基本类型存在默认值) * 持久化类通常建议手动添加一个无参构造函数 (因为有些操作是通过放射机制进行的) * 属性通常建议提供 getter/setter 方法 * 持久化类不能使用 final 修饰 * 持久化类中如果使用了集合类型数据,只能使用集合所对应的接口类型来声明(List/Map/Set) * 如下:ArrayList list = new ArrayList(); 不行 * List list = new ArrayList(); 可行 */ public class Person { private Integer id; private String name; private int age; private int passwork; private Date birthday; private Set<Address> addres = new HashSet<Address>(); public Person() { } public Person(String name, int age, int passwork, Date birthday) { super(); this.name = name; this.age = age; this.passwork = passwork; this.birthday = birthday; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + ", passwork=" + passwork + ", birthday=" + birthday + "]"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getPasswork() { return passwork; } public void setPasswork(int passwork) { this.passwork = passwork; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Set<Address> getAddres() { return addres; } public void setAddres(Set<Address> addres) { this.addres = addres; } }
package learn.hibernate.bean; public class Address { private Integer id; private String zipCode; private String address; private Person person; public Address() { } public Address(String zipCode, String address) { super(); this.zipCode = zipCode; this.address = address; } @Override public String toString() { return "Address [zipCode=" + zipCode + ", address=" + address + "]"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
2、映射配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="learn.hibernate.bean"> <class name="Person" table="t_person"> <id name="id" column="person_id"> <generator class="native"/> </id> <property name="name" column="t_name"/> <property name="age"/> <property name="passwork"/> <property name="birthday"/> <!-- 通过Set配置一个人对应的多个地址的关联关系 inverse=true 表示在Person端不维护关系;因为多的一端有外键,管理关联关系更高效 --> <set name="addres" cascade="all" inverse="true"> <!-- 指定 addres 集合中的数据对应t_person的的一个外键 --> <key column="p_id"/> <!-- 指定Person 关联的实例类型 --> <one-to-many class="Address"/> </set> <!-- inverse="false" 一定要为 false 因为要控制顺序 --> <!-- <list name="addres" inverse="false" cascade="all"> <key column="p_id"/> <index column="indexs" type="integer"/> <one-to-many class="Address"/> </list> --> <!-- inverse="false" 一定要为 false 因为要控制顺序 --> <!-- <map name="addres" inverse="false" cascade="all"> <key column="p_id"/> <index column="map_key" type="string"/> <one-to-many class="Address"/> </map> --> </class> <class name="Address" table="t_address"> <id name="id"> <generator class="native"/> </id> <property name="zipCode"/> <property name="address"/> <!-- 多的一端使用 many-to-one 进行配置 --> <many-to-one name="person" column="p_id"/> </class> </hibernate-mapping>
3、hibernate 配置文件:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <!--声明Hibernate配置文件的开始--> <hibernate-configuration> <!--表明以下的配置是针对session-factory配置的,SessionFactory是Hibernate中的一个类,这个类主要负责保存HIbernate的配置信息,以及对Session的操作--> <session-factory> <!--hibernate.dialect 只是Hibernate使用的数据库方言,就是要用Hibernate连接那种类型的数据库服务器--> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!--配置数据库的驱动程序,Hibernate 在连接数据库时,需要用到数据库的驱动程序--> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!--设置数据库的连接url:jdbc:mysql://localhost/hibernate,其中localhost表示mysql服务器名称,此处为本机, hibernate是数据库名--> <!-- jdbc:mysql://192.168.1.112:3305/hibernate 联网络数据库 jdbc:mysql:///hibernate 联本机 --> <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property> <!--连接数据库是用户名--> <property name="hibernate.connection.username">root</property> <!--连接数据库是密码--> <property name="hibernate.connection.password">123456</property> <!-- 是否自动创建数据库表 他主要有一下几个值: validate:当sessionFactory创建时,自动验证或者schema定义导入数据库。 create:每次启动都drop掉原来的schema,创建新的。 create-drop:当sessionFactory明确关闭时,drop掉schema。 update(常用):如果没有schema就创建,有就更新。 --> <property name="hibernate.hbm2ddl.auto">update</property> <!--是否在后台显示Hibernate用到的SQL语句,开发时设置为true,便于差错,程序运行时可以在Eclipse的控制台显示Hibernate的执行Sql语句。项目部署后可以设置为false,提高运行效率--> <property name="hibernate.show_sql">true</property> <!--指定映射文件 --> <mapping resource="learn\hibernate\bean\Person.hbm.xml"/> </session-factory> </hibernate-configuration>
4、测试类:
package learn.hibernate.test; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import learn.hibernate.bean.Person; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestHibernate { SessionFactory factory = null; Session session = null; Transaction tx = null; /** * 测试之前初始化数据 * @throws Exception */ @SuppressWarnings("deprecation") @Before public void setUp() throws Exception { System.out.println("---------初始化数据----------"); Configuration config = new Configuration().configure(); ServiceRegistry sr = new ServiceRegistryBuilder() .applySettings(config.getProperties()).buildServiceRegistry(); factory = config.buildSessionFactory(sr); session = factory.openSession(); } /** * 测试之后释放(销毁)数据 * @throws Exception */ @After public void tearDown() throws Exception { System.out.println("---------释放数据----------"); if(session.isOpen()){ session.close(); } } /** * 批量写入数据 */ @Test public void testAdd(){ tx = session.beginTransaction(); for(int i = 0; i < 100; i++){ Person person = new Person("ldh_"+i, 22+i, 123456+i, new Date()); session.persist(person); if(i % 10 == 0){ session.flush(); session.clear(); } } tx.commit(); } /** * 批量查询数据 */ @Test public void testQuery(){ // 查询表中的所有字段不需要写 select 关键字,如下: String queryString = "from Person"; Query query = session.createQuery(queryString); List<Person> list = query.list(); int count = 0; for(Person person : list){ System.out.println((++count)+"---------"+person); } } }
另一种查询方式:
/** * N + 1 查询 * 首先获取表的 id,当需要用到实体对象的时候,在根据 id 值获取每一条记录(延迟查询) * 每获得一条记录前都会发起一次查询,效率比较慢 * 通过下面的例子知道 iterator 支持一级缓存的读和写 */ @Test public void testQuery2(){ /*Person person = (Person)session.get(Person.class, 99); System.out.println(person); System.out.println("----------------------");*/ // 查询表中的所有字段不需要写 select 关键字,如下: String queryString = "from Person"; Query query = session.createQuery(queryString); Iterator<Person> it = query.iterate(); while(it.hasNext()){ Person p = it.next(); System.out.println(p); } // 没有发起查询,说明 person 对象是从缓存中拿到的 System.out.println("----------------------"); Person person = (Person)session.get(Person.class, 99); System.out.println(person); }
详解:list() 和 iterate() 方法的区别:
1、查询数据的不同形式:
(1)、Hibernate Iterator的查询本身是分为两步的:存在N+1问题
==> select id from cat //一条
==> select * from cat where id = ? //n条
解析:第一步,去数据库中取主键列表,第二步,按照主键一个一个取数据。假如不用JCS的话,那么从数据库中取出n条记录就需要n+1次sql查询,假如使用了JCS,他会先到JCS里面去看看,按照主键去找持久对象,假如有了,直接拿出来用,假如没有,那么只好去数据库中取得,然后再把它填到JCS里面去。
(2)、而Hibernate List方式:
==> select * from cat
它是JDBC的简单封装,一次sql就把所有的数据都取出来了,它不会像Iterator那样先取主键,然后再取数据,因此List无法利用JCS。不过List也可以把从数据库中取出的数据填充到JCS里面去。当再次使用list查询数据的时候,仍然要发送sql去数据库中读取数据,证明list()方法不使用缓存。
2、list和iterate对缓存的使用情况
(1)、iterate会查询session缓存、2级缓存, list只会使用查询缓存(如果查询缓存开启的话)
(2)、当查询缓存开启时,对iterate和list查询语句的影响
查询缓存:将query 的cache打开的话,缓存的是query本身,以hql 生成的 sql ,再加上参数,分页等信息做为key值, 而不是query的结果.query的结果是放在session的cache中和二 级cache中,和query的cache是有区别的。
以上文字来自:http://blog.csdn.net/javaloveiphone/article/details/8200691
二、HQL进阶
1、查询表的部分字段
/** * 查询单个字段 */ @Test public void testSelect(){ String hql = "select name from Person"; Query query = session.createQuery(hql); List<String> list = query.list(); for(String name : list){ System.out.println(name); } } /** * 查询多个字段 */ @Test public void testSelect2(){ String hql = "select name,age, passwork from Person"; Query query = session.createQuery(hql); // 如果查询的是多个字段,返回的是一个 Object[] 数组 List<Object[]> list = query.list(); for(Object[] objs : list){ System.out.println(Arrays.toString(objs)); } } /** * 查询多个字段 */ @Test public void testSelect3(){ // 可以在 hql 语句上使用构造器的方式来绑定查询数据(前提:这个构造器必须是已定义的) String hql = "select new Person(name,age, passwork, birthday) from Person"; Query query = session.createQuery(hql); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } } /** * 查询多个字段 */ @Test public void testSelect4(){ // 可以在 hql 语句上使用List的方式来绑定查询数据,并将匹配条件的数据返回成对应的List集合 String hql = "select new List(name,age, passwork, birthday) from Person"; Query query = session.createQuery(hql); List<List> list = query.list(); for(List l : list){ for(int i = 0, size = l.size(); i < size; i++){ System.out.println(l.get(i)); } System.out.println("------------------"); } } /** * 查询多个字段 */ @Test public void testSelect5(){ // 可以在 hql 语句上使用 Map 的方式来绑定查询数据,并将匹配条件的数据返回成对应的 Map 集合 String hql = "select new Map(name,age, passwork, birthday) from Person"; Query query = session.createQuery(hql); List<Map> list = query.list(); for(Map m : list){ Iterator<Map.Entry> it = m.entrySet().iterator(); while(it.hasNext()){ Map.Entry map = it.next(); System.out.println(map.getKey()+"----------------"+map.getValue()); } System.out.println("------------------"); } } /** * 查询多个字段 */ @Test public void testSelect6(){ // 可以在 hql 语句上使用 Map 的方式来绑定查询数据,并将匹配条件的数据返回成对应的 Map 集合 String hql = "select new Map(name as n,age as a, passwork as p, birthday as b) from Person"; Query query = session.createQuery(hql); List<Map> list = query.list(); for(Map m : list){ Iterator<Map.Entry> it = m.entrySet().iterator(); while(it.hasNext()){ Map.Entry map = it.next(); System.out.println(map.getKey()+"----------------"+map.getValue()); } System.out.println("------------------"); } }
2、查询某几条数据
/** * 查询某几条数据 */ @Test public void testSelect7(){ // 可以在 hql 语句上使用构造器的方式来绑定查询数据(前提:这个构造器必须是已定义的) String hql = "select new Person(name,age, passwork, birthday) from Person"; Query query = session.createQuery(hql); List<Person> list = query.list(); // 获取集合中的指定下标位置的数据,但是其他满足条件的数据已经查询出来,并写入的缓存中了 Person p = list.get(0); System.out.println(p); } /** * 查询满足条件的唯一记录 */ @Test public void testSelect8(){ // 可以在 hql 语句上使用构造器的方式来绑定查询数据(前提:这个构造器必须是已定义的) String hql = "select new Person(name,age, passwork, birthday) from Person"; Query query = session.createQuery(hql); // 如果知道获取的数据时唯一的一条记录可以调用uniqueResult()方法,如果有多条记录则报异常 Person p = (Person)query.uniqueResult(); System.out.println(p); }
3、分页查询
/** * 分页查询 */ @Test public void testSelect9(){ String hql = "from Person"; Query query = session.createQuery(hql); // 分页的起始位置(会从指定位置的下一个位置开始) query.setFirstResult(0); // 分页的大小 query.setMaxResults(10); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } }
4、绑定变量
/** * 绑定变量 */ @Test public void testSelect10(){ String hql = "from Person where id= ?"; Query query = session.createQuery(hql); // 通过 ?号占位符绑定变量,JDBC是从 1 开始,hibernate 是从 0 开始 query.setInteger(0, 99); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } } /** * 多条件绑定变量查询 */ @Test public void testSelect11(){ String hql = "from Person where id= ?1 and name = ?2"; Query query = session.createQuery(hql); // 参数的位置通过 ?后面的数字来确定,不管查询条件的位置如何变化都不会改变一下查询结果 query.setInteger("1", 99); query.setString("2", "admin-98"); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } } /** * 命名参数绑定查询 */ @Test public void testSelect12(){ // 命名参数绑定 String hql = "from Person where id= :id and name = :name"; Query query = session.createQuery(hql); query.setInteger("id", 99); query.setString("name", "admin-98"); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } } /** * 命名参数绑定多值查询 */ @Test public void testSelect13(){ // 命名参数绑定 String hql = "from Person where id in(:ids)"; Query query = session.createQuery(hql); // 批量参数绑定可以使用数组 //query.setParameterList("ids", new Object[]{3,6,9}); // 批量参数绑定可以使用集合 List<Integer> lists = new ArrayList<Integer>(); lists.add(3); lists.add(6); lists.add(9); query.setParameterList("ids", lists); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } }
5、模糊查询
/** * 模糊查询 */ @Test public void testSelect14(){ // 命名参数绑定 String hql = "from Person where name like :name"; Query query = session.createQuery(hql); query.setString("name", "%-99%"); List<Person> list = query.list(); for(Person p : list){ System.out.println(p); } }
6、连接查询
/** * 为了方便连接查询,往 Address 表中写入数据 */ @Test public void testSave2(){ Person p = (Person)session.get(Person.class, 1); Address a1 = new Address("420000", "湖南桂阳"); Address a2 = new Address("422300", "湖南郴州"); Set<Address> set = new HashSet<Address>(); set.add(a1); set.add(a2); // Person 与 Address 关联 p.setAddres(set); // Address 与 Person 关联 a1.setPerson(p); a1.setPerson(p); tx = session.beginTransaction(); session.update(p); tx.commit(); } /** * 连接查询 */ @Test public void testSelect15(){ //String sql ="select * from t_person p, t_address a where p.id = a.p_id and p.name = ‘admin-0‘"; String hql = "select p,a from t_person p, t_address a where p.id = a.person and p.name = :name"; Query query = session.createQuery(hql); query.setString("name", "admin-0"); List<Object[]> list = query.list(); for(Object [] obj : list){ System.out.println(Arrays.toString(obj)); } } /** * 连接查询 */ @Test public void testSelect16(){ // addres 对应 映射文件中 set元素的 name 属性值 String hql = "select p,a from t_person p left join p.addres a where p.name = :name"; Query query = session.createQuery(hql); query.setString("name", "admin-0"); List<Object[]> list = query.list(); for(Object [] obj : list){ System.out.println(Arrays.toString(obj)); } }
7、批量更新
/** * 批量更新 */ @Test public void testUpdate(){ String hql = "update Person set passwork = 1234543"; tx = session.beginTransaction(); int count = session.createQuery(hql).executeUpdate(); tx.commit(); String flag = count > 0?"成功":"失败"; System.out.println(flag); } /** * 批量更新 */ @Test public void testUpdate2(){ System.out.println("--------1------"); Person p = (Person)session.get(Person.class, 2); System.out.println(p); // 批量更新和批量删除,是不会影响缓存中的数据的 String hql = "update Person set passwork = 0 where id = 2"; tx = session.beginTransaction(); int count = session.createQuery(hql).executeUpdate(); tx.commit(); String flag = count > 0?"成功":"失败"; System.out.println(flag); // 清空缓存,以保证数据库表的数据和缓存中的数据是一致的 session.clear(); System.out.println("--------2------"); Person p2 = (Person)session.get(Person.class, 2); System.out.println(p2); }
8、删除
/** * 批量删除 */ @Test public void testDelete(){ String hql = "delete from Person where id > 1"; tx = session.beginTransaction(); // 映射文件中允许级联操作,但是 executeUpdate() 方法不支持级联操作,如果要想使用这个方法批量修改,那么需要加条件将不符合的数据排除 int count = session.createQuery(hql).executeUpdate(); tx.commit(); String flag = count > 0?"成功":"失败"; System.out.println(flag); } /** * 级联删除 */ @Test public void testCascadeDelete(){ Person p = (Person)session.get(Person.class, 1); tx = session.beginTransaction(); session.delete(p); tx.commit(); }
三、在持久化配置文件中配置HQL或SQL
第一种方式:
配置文件代码如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="learn.hibernate.bean"> <class name="Person" table="t_person"> <id name="id" column="person_id"> <generator class="native"/> </id> <property name="name" column="t_name"/> <property name="age"/> <property name="passwork"/> <property name="birthday"/> <set name="addres" cascade="all" inverse="true"> <!-- 指定 addres 集合中的数据对应t_person的的一个外键 --> <key column="p_id"/> <!-- 指定Person 关联的实例类型 --> <one-to-many class="Address"/> </set> </class> <class name="Address" table="t_address"> <id name="id"> <generator class="native"/> </id> <property name="zipCode"/> <property name="address"/> <many-to-one name="person" column="p_id"/> </class> <query name="myQuery"> from Person </query> </hibernate-mapping>
查询代码如下:
/** * 在 hibernate 开发中最好是将HQL 和标准的 SQL 查询语句尽量配置到持久化映射文件中(*.hbm.xml) * */ @Test public void testQuery(){ Query query = session.getNamedQuery("myQuery"); List<Person> list = query.list(); for(Person person : list){ System.out.println(person); } }
第二种方式:
单独写一个配置文件,代码如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="learn.hibernate.bean"> <query name="myQuery2"> from Person </query> <query name="myQuery3"> <!-- 直接写小于符合是不行的,这里用到小于符号的转义 --> from Person where id < 5 </query> <!-- 如果有特殊的字符与 xml中的起到冲突可以用以下方式解决 --> <query name="myQuery3"> <![CDDATE[from Person where id < :id]]> </query> </hibernate-mapping>
将配置文件引入到 *.cfg.xml 文件中,代码如下:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <!--声明Hibernate配置文件的开始--> <hibernate-configuration> <!--表明以下的配置是针对session-factory配置的,SessionFactory是Hibernate中的一个类,这个类主要负责保存HIbernate的配置信息,以及对Session的操作--> <session-factory> <!--hibernate.dialect 只是Hibernate使用的数据库方言,就是要用Hibernate连接那种类型的数据库服务器--> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!--配置数据库的驱动程序,Hibernate 在连接数据库时,需要用到数据库的驱动程序--> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!--设置数据库的连接url:jdbc:mysql://localhost/hibernate,其中localhost表示mysql服务器名称,此处为本机, hibernate是数据库名--> <!-- jdbc:mysql://192.168.1.112:3305/hibernate 联网络数据库 jdbc:mysql:///hibernate 联本机 --> <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property> <!--连接数据库是用户名--> <property name="hibernate.connection.username">root</property> <!--连接数据库是密码--> <property name="hibernate.connection.password">123456</property> <!-- 是否自动创建数据库表 他主要有一下几个值: validate:当sessionFactory创建时,自动验证或者schema定义导入数据库。 create:每次启动都drop掉原来的schema,创建新的。 create-drop:当sessionFactory明确关闭时,drop掉schema。 update(常用):如果没有schema就创建,有就更新。 --> <property name="hibernate.hbm2ddl.auto">update</property> <!--是否在后台显示Hibernate用到的SQL语句,开发时设置为true,便于差错,程序运行时可以在Eclipse的控制台显示Hibernate的执行Sql语句。项目部署后可以设置为false,提高运行效率--> <property name="hibernate.show_sql">true</property> <!--指定映射文件 --> <mapping resource="learn\hibernate\bean\Person.hbm.xml"/> <mapping resource="learn\hibernate\bean\query.xml"/> </session-factory> </hibernate-configuration>
Java 代码如下:
@Test public void testQuery2(){ Query query = session.getNamedQuery("myQuery4"); query.setInteger("id", 5); List<Person> list = query.list(); for(Person person : list){ System.out.println(person); } }
hibernate 的hql 知识可以查看一下网址:
http://blog.csdn.net/aaronuu/article/details/7034815
http://developer.51cto.com/art/200906/130045.htm
http://www.wyjava.com/html/2012/hibernate_0219/293.html