使用多对一关联映射,在查询业务账号时,自动查询出它对应的账务账号。
多对一关联映射开发步骤:
实现此案例需要按照如下步骤进行。
步骤一:在业务账号实体类中追加属性
在业务账号实体类Service中,追加Account类型的属性,用于封装它对应的唯一账务账号。由于这个属性包含了账务账号的ID,因此accountId属性可以去掉了,实际上这个属性必须去掉,否则会报错。代码如下:
package com.tarena.entity; import java.sql.Date; public class Service { private Integer id; #cold_bold// private Integer accountId; private String unixHost; private String osUserName; private String loginPassword; private String status; private Date createDate; private Date pauseDate; private Date closeDate; private Integer costId; #cold_bold // 追加属性,用于封装对应的Account记录 #cold_bold private Account account; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } #cold_bold// public Integer getAccountId() { #cold_bold// return accountId; #cold_bold// } #cold_bold// #cold_bold// public void setAccountId(Integer accountId) { #cold_bold// this.accountId = accountId; #cold_bold// } #cold_bold #cold_bold public Account getAccount() { #cold_bold return account; #cold_bold } #cold_bold #cold_bold public void setAccount(Account account) { #cold_bold this.account = account; #cold_bold } #cold_bold public String getUnixHost() { return unixHost; } public void setUnixHost(String unixHost) { this.unixHost = unixHost; } public String getOsUserName() { return osUserName; } public void setOsUserName(String osUserName) { this.osUserName = osUserName; } public String getLoginPassword() { return loginPassword; } public void setLoginPassword(String loginPassword) { this.loginPassword = loginPassword; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public Date getPauseDate() { return pauseDate; } public void setPauseDate(Date pauseDate) { this.pauseDate = pauseDate; } public Date getCloseDate() { return closeDate; } public void setCloseDate(Date closeDate) { this.closeDate = closeDate; } public Integer getCostId() { return costId; } public void setCostId(Integer costId) { this.costId = costId; } }
步骤二:在业务账号映射关系文件中配置这个属性
在业务账号映射关系文件service.hbm.xml中,配置这个关联属性,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Service" table="SERVICE"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">SERVICE_SEQ</param> </generator> </id> <!-- 由于account属性已经体现了业务账号与账务账号的关系, 并且account属性可以包含账务账号ID,因此accountId可以去掉, 实际上这里必须去掉这个属性的配置,否则会报错。 --> <!-- <property name="accountId" type="integer" column="ACCOUNT_ID"/> --> <property name="unixHost" type="string" column="UNIX_HOST"/> <property name="osUserName" type="string" column="OS_USERNAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="costId" type="integer" column="COST_ID"/> <!-- 配置account属性,采用多对一关系加载相关的account内容 --> <many-to-one name="account" column="ACCOUNT_ID" class="com.tarena.entity.Account"/> </class> </hibernate-mapping>
步骤三:创建测试类
在com.tarena.test包下,创建一个测试类TestManyToOne,并且增加一个测试方法。在方法中查询出一条业务账号数据,然后输出这个业务账号的一些属性,同时输出account属性的值。代码如下:
package com.tarena.test; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestManyToOne { @Test public void test1() { Session session = HibernateUtil.getSession(); Service service = (Service) session.get(Service.class, 2001); System.out.println(service.getOsUserName()); System.out.println("------------------"); System.out.println( service.getAccount().getId() + " " + service.getAccount().getIdcardNo()); } }
步骤四:测试
执行这个测试方法,控制台输出结果如下图,可以看出在查询SERVICE的同时,Hibernate自动查询出了它对应的ACCOUNT数据,并且这个关联查询是采用延迟加载机制实现的。
图-1
以下为本案例的完整代码。
其中业务账号实体类完整代码如下:
package com.tarena.entity; import java.sql.Date; public class Service { private Integer id; // private Integer accountId; private String unixHost; private String osUserName; private String loginPassword; private String status; private Date createDate; private Date pauseDate; private Date closeDate; private Integer costId; // 追加属性,用于封装关的Account记录 private Account account; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } // public Integer getAccountId() { // return accountId; // } // // public void setAccountId(Integer accountId) { // this.accountId = accountId; // } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public String getUnixHost() { return unixHost; } public void setUnixHost(String unixHost) { this.unixHost = unixHost; } public String getOsUserName() { return osUserName; } public void setOsUserName(String osUserName) { this.osUserName = osUserName; } public String getLoginPassword() { return loginPassword; } public void setLoginPassword(String loginPassword) { this.loginPassword = loginPassword; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public Date getPauseDate() { return pauseDate; } public void setPauseDate(Date pauseDate) { this.pauseDate = pauseDate; } public Date getCloseDate() { return closeDate; } public void setCloseDate(Date closeDate) { this.closeDate = closeDate; } public Integer getCostId() { return costId; } public void setCostId(Integer costId) { this.costId = costId; } }
业务账号映射关系文件代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Service" table="SERVICE"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">SERVICE_SEQ</param> </generator> </id> <!-- 由于account属性已经体现了业务账号与账务账号的关系, 并且account属性可以包含账务账号ID,因此accountId可以去掉, 实际上这里必须去掉这个属性的配置,否则会报错。 --> <!-- <property name="accountId" type="integer" column="ACCOUNT_ID"/> --> <property name="unixHost" type="string" column="UNIX_HOST"/> <property name="osUserName" type="string" column="OS_USERNAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="costId" type="integer" column="COST_ID"/> <!-- 配置account属性,采用多对一关系加载相关的account内容 --> <many-to-one name="account" column="ACCOUNT_ID" class="com.tarena.entity.Account"/> </class> </hibernate-mapping>
测试类代码如下:
package com.tarena.test; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestManyToOne { @Test public void test1() { Session session = HibernateUtil.getSession(); Service service = (Service) session.get(Service.class, 2001); System.out.println(service.getOsUserName()); System.out.println("------------------"); System.out.println( service.getAccount().getId() + " " + service.getAccount().getIdcardNo()); } }
请按照如下的方式使用关联映射:
可以按照如下的方式实现上述问题的要求:
实现此案例需要按照如下步骤进行。
步骤一:创建项目
复制项目HibernateDay02,粘贴并修改项目名为HibernateDay03。
步骤二:将一对多关联映射取消延迟加载
修改HibernateDay03项目中的Account.hbm.xml文件,将services属性的配置追加lazy=”false”,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> #cold_bold <set name="services" lazy="false"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出在查询账务账号之后Hibernate立刻查询了它对应的业务账号,已经取消了延迟加载。
图-2
步骤三:将多对一关联映射取消延迟加载
修改Service.hbm.xml,将account属性的配置追加lazy=”false”,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Service" table="SERVICE"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">SERVICE_SEQ</param> </generator> </id> <!-- 由于account属性已经体现了业务账号与账务账号的关系, 并且account属性可以包含账务账号ID,因此accountId可以去掉, 实际上这里必须去掉这个属性的配置,否则会报错。 --> <!-- <property name="accountId" type="integer" column="ACCOUNT_ID"/> --> <property name="unixHost" type="string" column="UNIX_HOST"/> <property name="osUserName" type="string" column="OS_USERNAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="costId" type="integer" column="COST_ID"/> <!-- 配置account属性,采用多对一关系加载相关的account内容 --> #cold_bold <many-to-one name="account" column="ACCOUNT_ID" #cold_bold class="com.tarena.entity.Account" #cold_bold lazy="false"/> </class> </hibernate-mapping>
执行TestManyToOne中的测试方法,控制台输出效果如下图,可以看出在查询业务账号之后Hibernate立刻查询了对应的账务账号,取消了延迟加载。
图-3
步骤四:将一对多关联映射设置为join方式抓取数据
修改Account.hbm.xml,将services属性的配置追加fetch=“join”,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> #cold_bold <set name="services" lazy="false" fetch="join"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。
图-4
步骤五:将多对一关联映射设置为join方式抓取数据
修改Service.hbm.xml,将account属性追加fetch=“join”,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Service" table="SERVICE"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">SERVICE_SEQ</param> </generator> </id> <!-- 由于account属性已经体现了业务账号与账务账号的关系, 并且account属性可以包含账务账号ID,因此accountId可以去掉, 实际上这里必须去掉这个属性的配置,否则会报错。 --> <!-- <property name="accountId" type="integer" column="ACCOUNT_ID"/> --> <property name="unixHost" type="string" column="UNIX_HOST"/> <property name="osUserName" type="string" column="OS_USERNAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="costId" type="integer" column="COST_ID"/> <!-- 配置account属性,采用多对一关系加载相关的account内容 --> #cold_bold <many-to-one name="account" column="ACCOUNT_ID" #cold_bold class="com.tarena.entity.Account" #cold_bold lazy="false" fetch="join"/> </class> </hibernate-mapping>
执行TestManyToOne中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。
图-5
以下为本案例的完整代码。
其中账务账号映射关系文件完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> <set name="services" lazy="false" fetch="join"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
业务账号映射关系文件完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Service" table="SERVICE"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">SERVICE_SEQ</param> </generator> </id> <!-- 由于account属性已经体现了业务账号与账务账号的关系, 并且account属性可以包含账务账号ID,因此accountId可以去掉, 实际上这里必须去掉这个属性的配置,否则会报错。 --> <!-- <property name="accountId" type="integer" column="ACCOUNT_ID"/> --> <property name="unixHost" type="string" column="UNIX_HOST"/> <property name="osUserName" type="string" column="OS_USERNAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="costId" type="integer" column="COST_ID"/> <!-- 配置account属性,采用多对一关系加载相关的account内容 --> <many-to-one name="account" column="ACCOUNT_ID" class="com.tarena.entity.Account" lazy="false" fetch="join"/> </class> </hibernate-mapping>
使用级联添加/修改,在添加/修改账务账号时,自动添加/修改其对应的业务账号。
通过在映射关系文件中设置cascade=”save-update”,可以支持级联添加/修改。
实现此案例需要按照如下步骤进行。
步骤一:在账务账号映射关系文件中设置级联添加/修改
在账务账号映射关系文件Account.hbm.xml中,通过cascade=“save-update”设置支持级联添加/修改,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> #cold_bold <set name="services" #cold_bold lazy="false" fetch="join" #cold_bold cascade="save-update"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
步骤二:测试级联添加
在com.tarena.test包下,创建测试类TestCascade,并在类中增加测试级联添加的方法,代码如下:
package com.tarena.test; import java.util.HashSet; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.entity.Account; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestCascade { /** * 级联添加 */ @Test public void test1() { // 模拟要添加的账务账号 Account a = new Account(); a.setLoginName("gg"); a.setLoginPassword("123"); a.setRealName("gg"); a.setIdcardNo("120392198410282549"); a.setStatus("0"); a.setTelephone("110"); // 模拟要添加的业务账号 Service s1 = new Service(); s1.setAccount(a); s1.setOsUserName("gg1"); s1.setLoginPassword("123"); s1.setUnixHost("192.168.1.1"); s1.setCostId(5); s1.setStatus("0"); Service s2 = new Service(); s2.setAccount(a); s2.setOsUserName("gg2"); s2.setLoginPassword("123"); s2.setUnixHost("192.168.1.2"); s2.setCostId(5); s2.setStatus("0"); // 将业务账号与账务账号关联 a.setServices(new HashSet<Service>()); a.getServices().add(s1); a.getServices().add(s2); Session session = HibernateUtil.getSession(); Transaction ts = session.beginTransaction(); try { session.save(a); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } }
执行test1()方法,控制台输出结果如下图,可以看出在新增账务账号之后,Hibernate自动新增了账务账号对应的业务账号数据,这就是级联添加所起到的作用。
图-6
此时,查询账务账号表,数据如下图,其中id=380的行就是刚刚添加的行。
图-7
再查询业务账号表,数据如下图,其中account_id=380的行就是级联添加的行。
图-8
步骤三:测试级联修改
在TestCascade测试类中,增加测试级联修改的方法,代码如下:
package com.tarena.test; import java.util.HashSet; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.entity.Account; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestCascade { /** * 级联添加 */ @Test public void test1() { // 模拟要添加的账务账号 Account a = new Account(); a.setLoginName("gg"); a.setLoginPassword("123"); a.setRealName("gg"); a.setIdcardNo("120392198410282549"); a.setStatus("0"); a.setTelephone("110"); // 模拟要添加的业务账号 Service s1 = new Service(); s1.setAccount(a); s1.setOsUserName("gg1"); s1.setLoginPassword("123"); s1.setUnixHost("192.168.1.1"); s1.setCostId(5); s1.setStatus("0"); Service s2 = new Service(); s2.setAccount(a); s2.setOsUserName("gg2"); s2.setLoginPassword("123"); s2.setUnixHost("192.168.1.2"); s2.setCostId(5); s2.setStatus("0"); // 将业务账号与账务账号关联 a.setServices(new HashSet<Service>()); a.getServices().add(s1); a.getServices().add(s2); Session session = HibernateUtil.getSession(); Transaction ts = session.beginTransaction(); try { session.save(a); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } #cold_bold /** #cold_bold * 级联修改 #cold_bold */ #cold_bold @Test #cold_bold public void test2() { #cold_bold Session session = HibernateUtil.getSession(); #cold_bold // 查询出要修改的账务账号 #cold_bold Account account = #cold_bold (Account) session.get(Account.class, 380); #cold_bold // 模拟对账务账号的修改 #cold_bold account.setLoginName("pp"); #cold_bold Set<Service> services = account.getServices(); #cold_bold for (Service service : services) { #cold_bold // 模拟对业务账号的修改 #cold_bold service.setLoginPassword("pp"); #cold_bold } #cold_bold Transaction ts = session.beginTransaction(); #cold_bold try { #cold_bold session.update(account); #cold_bold ts.commit(); #cold_bold } catch (HibernateException e) { #cold_bold e.printStackTrace(); #cold_bold ts.rollback(); #cold_bold } finally { #cold_bold session.close(); #cold_bold } #cold_bold } }
执行test2()方法后,控制台输出的结果如下图,可以看出在修改完账务账号之后,Hibernate自动修改了它对应的业务账号数据。
图-9
此时,查询账务账号表,id=380的行已经发生了改变。
图-10
再查询业务账号表,account_id=380的行也发生了改变。
图-11
以下为本案例的完整代码。
其中Account.hbm.xml完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> <set name="services" lazy="false" fetch="join" cascade="save-update"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
TestCascade完整代码如下:
package com.tarena.test; import java.util.HashSet; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.entity.Account; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestCascade { /** * 级联添加 */ @Test public void test1() { // 模拟要添加的账务账号 Account a = new Account(); a.setLoginName("gg"); a.setLoginPassword("123"); a.setRealName("gg"); a.setIdcardNo("120392198410282549"); a.setStatus("0"); a.setTelephone("110"); // 模拟要添加的业务账号 Service s1 = new Service(); s1.setAccount(a); s1.setOsUserName("gg1"); s1.setLoginPassword("123"); s1.setUnixHost("192.168.1.1"); s1.setCostId(5); s1.setStatus("0"); Service s2 = new Service(); s2.setAccount(a); s2.setOsUserName("gg2"); s2.setLoginPassword("123"); s2.setUnixHost("192.168.1.2"); s2.setCostId(5); s2.setStatus("0"); // 将业务账号与账务账号关联 a.setServices(new HashSet<Service>()); a.getServices().add(s1); a.getServices().add(s2); Session session = HibernateUtil.getSession(); Transaction ts = session.beginTransaction(); try { session.save(a); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } /** * 级联修改 */ @Test public void test2() { Session session = HibernateUtil.getSession(); // 查询出要修改的账务账号 Account account = (Account) session.get(Account.class, 380); // 模拟对账务账号的修改 account.setLoginName("pp"); Set<Service> services = account.getServices(); for (Service service : services) { // 模拟对业务账号的修改 service.setLoginPassword("pp"); } Transaction ts = session.beginTransaction(); try { session.update(account); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } }
使用级联删除,在删除账务账号时,自动删除其对应的业务账号。
通过在映射关系文件中设置cascade=“delete”,可以支持级联删除,如果想全面支持级联添加、修改和删除,可以设置cascade=“all”。
实现此案例需要按照如下步骤进行。
步骤一:在账务账号映射关系文件中设置级联删除
在账务账号映射关系文件Account.hbm.xml中,通过cascade=“all”设置支持级联删除,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> #cold_bold <set name="services" #cold_bold lazy="false" fetch="join" #cold_bold cascade="all"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
步骤二:测试级联删除
在TestCascade测试类中,增加测试级联删除的方法,代码如下:
package com.tarena.test; import java.util.HashSet; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.entity.Account; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestCascade { /** * 级联添加 */ @Test public void test1() { // 模拟要添加的账务账号 Account a = new Account(); a.setLoginName("gg"); a.setLoginPassword("123"); a.setRealName("gg"); a.setIdcardNo("120392198410282549"); a.setStatus("0"); a.setTelephone("110"); // 模拟要添加的业务账号 Service s1 = new Service(); s1.setAccount(a); s1.setOsUserName("gg1"); s1.setLoginPassword("123"); s1.setUnixHost("192.168.1.1"); s1.setCostId(5); s1.setStatus("0"); Service s2 = new Service(); s2.setAccount(a); s2.setOsUserName("gg2"); s2.setLoginPassword("123"); s2.setUnixHost("192.168.1.2"); s2.setCostId(5); s2.setStatus("0"); // 将业务账号与账务账号关联 a.setServices(new HashSet<Service>()); a.getServices().add(s1); a.getServices().add(s2); Session session = HibernateUtil.getSession(); Transaction ts = session.beginTransaction(); try { session.save(a); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } /** * 级联修改 */ @Test public void test2() { Session session = HibernateUtil.getSession(); // 查询出要修改的账务账号 Account account = (Account) session.get(Account.class, 380); // 模拟对账务账号的修改 account.setLoginName("pp"); Set<Service> services = account.getServices(); for (Service service : services) { // 模拟对业务账号的修改 service.setLoginPassword("pp"); } Transaction ts = session.beginTransaction(); try { session.update(account); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } #cold_bold /** #cold_bold * 级联删除 #cold_bold */ #cold_bold @Test #cold_bold public void test3() { #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Account account = (Account) session.get(Account.class, 380); #cold_bold Transaction ts = session.beginTransaction(); #cold_bold try { #cold_bold session.delete(account); #cold_bold ts.commit(); #cold_bold } catch (HibernateException e) { #cold_bold e.printStackTrace(); #cold_bold ts.rollback(); #cold_bold } finally { #cold_bold session.close(); #cold_bold } #cold_bold } }
步骤三:测试
执行test3()方法,控制台输出结果如下图,可以看出程序在运行时报错了:
图-12
步骤四:排错
上述错误是执行第二个SQL时产生的,这个SQL的目的是维护关系字段,将其置为null,而这个外键字段存在非空约束,因此报错。类似的事情在级联添加时也看到过,参考图-6,不同的是级联添加时要将关联字段设置为新生成的账务账号ID。然而,在级联新增或删除时业务账号时,业务账号本身已经维护好了关联字段,因此这个额外的操作是多余的,可以去掉,我们可以在关联属性上通过inverse=“true“去掉这个行为,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> #cold_bold <set name="services" #cold_bold lazy="false" fetch="join" #cold_bold cascade="all" inverse="true"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
步骤五:测试
再次执行test3()方法,控制台输出结果如下图,可以看出本次删除成功了,不但删除了账务账号数据,在此之前还删除了它所对应的所有业务账号。
图-13
以下为本案例的完整代码。
其中Account.hbm.xml完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tarena.entity.Account" table="ACCOUNT"> <id name="id" type="integer" column="id"> <generator class="sequence"> <param name="sequence">ACCOUNT_SEQ</param> </generator> </id> <property name="recommenderId" type="integer" column="RECOMMENDER_ID"/> <property name="loginName" type="string" column="LOGIN_NAME"/> <property name="loginPassword" type="string" column="LOGIN_PASSWD"/> <property name="status" type="string" column="STATUS"/> <property name="createDate" type="date" column="CREATE_DATE"/> <property name="pauseDate" type="date" column="PAUSE_DATE"/> <property name="closeDate" type="date" column="CLOSE_DATE"/> <property name="realName" type="string" column="REAL_NAME"/> <property name="idcardNo" type="string" column="IDCARD_NO"/> <property name="birthdate" type="date" column="BIRTHDATE"/> <property name="gender" type="string" column="GENDER"/> <property name="occupation" type="string" column="OCCUPATION"/> <property name="telephone" type="string" column="TELEPHONE"/> <property name="email" type="string" column="EMAIL"/> <property name="mailaddress" type="string" column="MAILADDRESS"/> <property name="zipcode" type="string" column="ZIPCODE"/> <property name="qq" type="string" column="QQ"/> <property name="lastLoginTime" type="date" column="LAST_LOGIN_TIME"/> <property name="lastLoginIp" type="string" column="LAST_LOGIN_IP"/> <!-- 配置services属性,采用一对多的关系 --> <set name="services" lazy="false" fetch="join" cascade="all" inverse="true"> <!-- 用于指定关联条件,写关联条件的外键字段 --> <key column="ACCOUNT_ID"/> <!-- 用于指定采用哪种关系,加载哪方数据 --> <one-to-many class="com.tarena.entity.Service"/> </set> </class> </hibernate-mapping>
TestCascade完整代码如下:
package com.tarena.test; import java.util.HashSet; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.entity.Account; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestCascade { /** * 级联添加 */ @Test public void test1() { // 模拟要添加的账务账号 Account a = new Account(); a.setLoginName("gg"); a.setLoginPassword("123"); a.setRealName("gg"); a.setIdcardNo("120392198410282549"); a.setStatus("0"); a.setTelephone("110"); // 模拟要添加的业务账号 Service s1 = new Service(); s1.setAccount(a); s1.setOsUserName("gg1"); s1.setLoginPassword("123"); s1.setUnixHost("192.168.1.1"); s1.setCostId(5); s1.setStatus("0"); Service s2 = new Service(); s2.setAccount(a); s2.setOsUserName("gg2"); s2.setLoginPassword("123"); s2.setUnixHost("192.168.1.2"); s2.setCostId(5); s2.setStatus("0"); // 将业务账号与账务账号关联 a.setServices(new HashSet<Service>()); a.getServices().add(s1); a.getServices().add(s2); Session session = HibernateUtil.getSession(); Transaction ts = session.beginTransaction(); try { session.save(a); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } /** * 级联修改 */ @Test public void test2() { Session session = HibernateUtil.getSession(); // 查询出要修改的账务账号 Account account = (Account) session.get(Account.class, 380); // 模拟对账务账号的修改 account.setLoginName("pp"); Set<Service> services = account.getServices(); for (Service service : services) { // 模拟对业务账号的修改 service.setLoginPassword("pp"); } Transaction ts = session.beginTransaction(); try { session.update(account); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } /** * 级联删除 */ @Test public void test3() { Session session = HibernateUtil.getSession(); Account account = (Account) session.get(Account.class, 380); Transaction ts = session.beginTransaction(); try { session.delete(account); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); } finally { session.close(); } } }
使用带条件的HQL 查询业务账号数据。
在HQL中拼入条件,如name=?,然后在查询之前使用query给参数赋值。
实现此案例需要按照如下步骤进行。
步骤一:编写按条件查询方法
在com.tarena.test包下创建测试类TestHQL,并在类中增加按条件查询的测试方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { /** * 按条件查询 */ @Test public void test1() { String hql = "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Service> services = query.list(); for(Service service : services){ System.out.println(service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } }
步骤二:测试
执行test1()方法,控制台输出效果如下图,可以看到按条件查询出的结果:
图-14
以下为本案例的完整代码。
其中TestHQL完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { /** * 按条件查询 */ @Test public void test1() { String hql = "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Service> services = query.list(); for(Service service : services){ System.out.println(service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } }
使用HQL查询,要求只查询一部分字段。
可以通过select子句明确指定要返回的字段,注意HQL中select子句中写的是属性,而不是表中的字段。
实现此案例需要按照如下步骤进行。
步骤一:编写查询一部分字段方法
在TestHQL中增加查询一部分字段的方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 查询一部分字段 #cold_bold */ #cold_bold @Test #cold_bold public void test2() { #cold_bold String hql = "select id,unixHost,osUserName " + #cold_bold "from Service where unixHost=?"; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold query.setString(0, "192.168.0.20"); #cold_bold List<Object[]> services = query.list(); #cold_bold for(Object[] service : services) { #cold_bold System.out.println(service[0] #cold_bold + " " + service[1] #cold_bold + " " + service[2]); #cold_bold } #cold_bold session.close(); #cold_bold } }
步骤二:测试
执行test2(),控制台输出结果如下图,可以看到查询出来的这些字段的值。
图-15
以下为本案例的完整代码。
其中TestHQL完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { /** * 按条件查询 */ @Test public void test1() { String hql = "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Service> services = query.list(); for(Service service : services){ System.out.println(service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } /** * 查询一部分字段 */ @Test public void test2() { String hql = "select id,unixHost,osUserName " + "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Object[]> services = query.list(); for(Object[] service : services) { System.out.println(service[0] + " " + service[1] + " " + service[2]); } session.close(); } }
按照每页显示3条数据,查询第1页的条件,查询出业务账号表中满足条件的记录,并且查询出总页数。
实现此案例需要按照如下步骤进行。
步骤一:编写分页查询的方法
在TestHQL中,增加分页查询的方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 分页查询 #cold_bold */ #cold_bold @Test #cold_bold public void test3() { #cold_bold int page = 1; #cold_bold int pageSize = 3; #cold_bold #cold_bold String hql = "from Service order by id"; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold // 追加分页参数设置 #cold_bold int from = (page - 1) * pageSize; #cold_bold query.setFirstResult(from);// 设置起点,从0开始 #cold_bold query.setMaxResults(pageSize);// 设置页容量 #cold_bold List<Service> services = query.list(); #cold_bold for (Service service : services) { #cold_bold System.out.println( #cold_bold service.getId() + " " #cold_bold + service.getUnixHost() + " " #cold_bold + service.getOsUserName()); #cold_bold } #cold_bold session.close(); #cold_bold } }
步骤二:测试
执行test3(),控制台输出结果如下图,输出了第1页的3条数据。
图-16
步骤三:编写查询总页数的方法
在TestHQL中,增加查询总页数的方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 查询总页数 #cold_bold */ #cold_bold @Test #cold_bold public void test4() { #cold_bold int pageSize=3; #cold_bold String hql = "select count(*) from Service"; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold int rows = Integer.parseInt(query.uniqueResult().toString()); #cold_bold int totalPages = 0; #cold_bold if(rows%pageSize == 0) { #cold_bold totalPages = rows/pageSize; #cold_bold } else { #cold_bold totalPages = rows/pageSize+1; #cold_bold } #cold_bold System.out.println(totalPages); #cold_bold session.close(); #cold_bold } }
步骤四:测试
执行test4(),控制台输出结果如下图,输出了总页数。
图-17
以下为本案例的完整代码。
其中TestHQL完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { /** * 按条件查询 */ @Test public void test1() { String hql = "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Service> services = query.list(); for(Service service : services){ System.out.println(service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } /** * 查询一部分字段 */ @Test public void test2() { String hql = "select id,unixHost,osUserName " + "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Object[]> services = query.list(); for(Object[] service : services) { System.out.println(service[0] + " " + service[1] + " " + service[2]); } session.close(); } /** * 分页查询 */ @Test public void test3() { int page = 1; int pageSize = 3; String hql = "from Service order by id"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); // 追加分页参数设置 int from = (page - 1) * pageSize; query.setFirstResult(from);// 设置起点,从0开始 query.setMaxResults(pageSize);// 设置页容量 List<Service> services = query.list(); for (Service service : services) { System.out.println( service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } /** * 查询总页数 */ @Test public void test4() { int pageSize=3; String hql = "select count(*) from Service"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); int rows = Integer.parseInt(query.uniqueResult().toString()); int totalPages = 0; if(rows%pageSize == 0) { totalPages = rows/pageSize; } else { totalPages = rows/pageSize+1; } System.out.println(totalPages); session.close(); } }
使用HQL,联合查询出业务账号与账务账号的数据。
HQL中多表联合查询的方式有3种,分别是
使用这三种方式来做业务账号与账务账号的联合查询。
实现此案例需要按照如下步骤进行。
步骤一:编写对象方式关联的方法
在TestHQL中,增加对象方式关联的查询方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 多表联合查询-对象方式关联 #cold_bold */ #cold_bold @Test #cold_bold public void test5() { #cold_bold String hql = "select " + #cold_bold "s.id," + #cold_bold "s.osUserName," + #cold_bold "s.unixHost, " + #cold_bold "a.id,a.realName," + #cold_bold "a.idcardNo " + #cold_bold "from Service s,Account a " + #cold_bold "where s.account.id=a.id "; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold List<Object[]> list = query.list(); #cold_bold for (Object[] objs : list) { #cold_bold System.out.println( #cold_bold objs[0] + " " + #cold_bold objs[1] + " " + #cold_bold objs[2] + " " + #cold_bold objs[3] + " " + #cold_bold objs[4] + " " + #cold_bold objs[5]); #cold_bold } #cold_bold session.close(); #cold_bold } }
步骤二:测试
执行test5(),控制台输出结果如下图,可以查询出关联的数据。
图-18
步骤三:编写join方式关联的方法
在TestHQL中,增加join方式关联的查询方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 多表联合查询-join方式关联 #cold_bold */ #cold_bold @Test #cold_bold public void test6() { #cold_bold String hql = "select " + #cold_bold "s.id,s.osUserName," + #cold_bold "s.unixHost, " + #cold_bold "a.id," + #cold_bold "a.realName," + #cold_bold "a.idcardNo " + #cold_bold "from Service s join s.account a "; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold List<Object[]> list = query.list(); #cold_bold for (Object[] objs : list) { #cold_bold System.out.println( #cold_bold objs[0] + " " + #cold_bold objs[1] + " " + #cold_bold objs[2] + " " + #cold_bold objs[3] + " " + #cold_bold objs[4] + " " + #cold_bold objs[5]); #cold_bold } #cold_bold session.close(); #cold_bold } }
步骤四:测试
执行test6(),控制台输出结果如下图,可以查询出关联的数据。
图-19
步骤五:编写select子句关联的方法
在TestHQL中,增加select子句关联的查询方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { // 其他查询方法略 #cold_bold /** #cold_bold * 多表联合查询-select子句关联 #cold_bold */ #cold_bold @Test #cold_bold public void test7() { #cold_bold String hql = "select " + #cold_bold "id," + #cold_bold "osUserName," + #cold_bold "unixHost, " + #cold_bold "account.id," + #cold_bold "account.realName," + #cold_bold "account.idcardNo " + #cold_bold "from Service "; #cold_bold Session session = HibernateUtil.getSession(); #cold_bold Query query = session.createQuery(hql); #cold_bold List<Object[]> list = query.list(); #cold_bold for (Object[] objs : list) { #cold_bold System.out.println( #cold_bold objs[0] + " " + #cold_bold objs[1] + " " + #cold_bold objs[2] + " " + #cold_bold objs[3] + " " + #cold_bold objs[4] + " " + #cold_bold objs[5]); #cold_bold } #cold_bold session.close(); #cold_bold } }
步骤六:测试
执行test7(),控制台输出结果如下,可以查询出关联的数据。
图-20
以下为本案例的完整代码。
其中TestHQL完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestHQL { /** * 按条件查询 */ @Test public void test1() { String hql = "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Service> services = query.list(); for(Service service : services){ System.out.println(service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } /** * 查询一部分字段 */ @Test public void test2() { String hql = "select id,unixHost,osUserName " + "from Service where unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString(0, "192.168.0.20"); List<Object[]> services = query.list(); for(Object[] service : services) { System.out.println(service[0] + " " + service[1] + " " + service[2]); } session.close(); } /** * 分页查询 */ @Test public void test3() { int page = 1; int pageSize = 3; String hql = "from Service order by id"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); // 追加分页参数设置 int from = (page - 1) * pageSize; query.setFirstResult(from);// 设置起点,从0开始 query.setMaxResults(pageSize);// 设置页容量 List<Service> services = query.list(); for (Service service : services) { System.out.println( service.getId() + " " + service.getUnixHost() + " " + service.getOsUserName()); } session.close(); } /** * 查询总页数 */ @Test public void test4() { int pageSize=3; String hql = "select count(*) from Service"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); int rows = Integer.parseInt(query.uniqueResult().toString()); int totalPages = 0; if(rows%pageSize == 0) { totalPages = rows/pageSize; } else { totalPages = rows/pageSize+1; } System.out.println(totalPages); session.close(); } /** * 多表联合查询-对象方式关联 */ @Test public void test5() { String hql = "select " + "s.id," + "s.osUserName," + "s.unixHost, " + "a.id,a.realName," + "a.idcardNo " + "from Service s,Account a " + "where s.account.id=a.id "; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); List<Object[]> list = query.list(); for (Object[] objs : list) { System.out.println( objs[0] + " " + objs[1] + " " + objs[2] + " " + objs[3] + " " + objs[4] + " " + objs[5]); } session.close(); } /** * 多表联合查询-join方式关联 */ @Test public void test6() { String hql = "select " + "s.id,s.osUserName," + "s.unixHost, " + "a.id," + "a.realName," + "a.idcardNo " + "from Service s join s.account a "; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); List<Object[]> list = query.list(); for (Object[] objs : list) { System.out.println( objs[0] + " " + objs[1] + " " + objs[2] + " " + objs[3] + " " + objs[4] + " " + objs[5]); } session.close(); } /** * 多表联合查询-select子句关联 */ @Test public void test7() { String hql = "select " + "id," + "osUserName," + "unixHost, " + "account.id," + "account.realName," + "account.idcardNo " + "from Service "; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); List<Object[]> list = query.list(); for (Object[] objs : list) { System.out.println( objs[0] + " " + objs[1] + " " + objs[2] + " " + objs[3] + " " + objs[4] + " " + objs[5]); } session.close(); } }
在Hibernate中直接使用SQL查询业务账号数据。
Hibernate中可以通过SQLQuery对象来执行原始的SQL。
实现此案例需要按照如下步骤进行。
步骤一:编写使用SQL查询的方法
在com.tarena.test包下,创建测试类TestOtherQuery,并在类中增加使用SQL查询的方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestOtherQuery { /** * 使用SQL查询 */ @Test public void test1() { String sql = "select * " + "from SERVICE where unix_host=?"; Session session = HibernateUtil.getSession(); SQLQuery query = session.createSQLQuery(sql); query.setString(0, "192.168.0.20"); query.addEntity(Service.class); //采用Service类型封装一条数据 List<Service> list = query.list(); for(Service service : list){ System.out.println( service.getId() + " " + service.getOsUserName() + " " + service.getUnixHost() + " " ); } session.close(); } }
步骤二:测试
执行test1(),控制台输出结果如下图,是直接执行SQL所查询到的内容:
图-21
以下为本案例的完整代码。
其中TestOtherQuery完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Service; import com.tarena.util.HibernateUtil; public class TestOtherQuery { /** * 使用SQL查询 */ @Test public void test1() { String sql = "select * " + "from SERVICE where unix_host=?"; Session session = HibernateUtil.getSession(); SQLQuery query = session.createSQLQuery(sql); query.setString(0, "192.168.0.20"); query.addEntity(Service.class); //采用Service类型封装一条数据 List<Service> list = query.list(); for(Service service : list){ System.out.println( service.getId() + " " + service.getOsUserName() + " " + service.getUnixHost() + " " ); } session.close(); } }
在查询员工时,使用二级缓存,使得不同的session可以共享这些员工数据。
使用ehcache缓存组件来支持二级缓存,二级缓存的使用步骤为
实现此案例需要按照如下步骤进行。
步骤一:导入二级缓存驱动包
导入二级缓存驱动包ehcache-1.2.3.jar,导入后项目中包结构如下图:
图-22
步骤二:引入二级缓存配置文件
引入二级缓存配置文件ehcache.xml,并配置缓存参数,代码如下:
<ehcache> <!-- 缓存到硬盘时的缓存路径,java.io.tmpdir表示 系统默认缓存路径。 --> <diskStore path="java.io.tmpdir"/> <!-- 默认缓存配置。 maxElementsInMemory: 二级缓存可容纳最大对象数。 eternal: 是否保持二级缓存中对象不变。 timeToIdleSeconds: 允许对象空闲的时间,即对象最后一次访问起,超过该时间即失效。 timeToLiveSeconds: 允许对象存活的时间,即对象创建起,超过该时间即失效。 overflowToDisk: 内存不足时,是否允许使用硬盘缓存,写入路径参见diskStore。 --> <defaultCache maxElementsInMemory="300" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true" /> <!-- 自定义缓存配置 --> <cache name="myCache" maxElementsInMemory="2000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>
步骤三:开启二级缓存、指定二级缓存驱动类
在hibernate.cfg.xml中开启二级缓存,并指定二级缓存驱动类,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息,根据自己的数据库进行配置 --> <property name="connection.url"> jdbc:oracle:thin:@localhost:1521:xe </property> <property name="connection.username">lhh</property> <property name="connection.password">123456</property> <property name="connection.driver_class"> oracle.jdbc.OracleDriver </property> <!-- Hibernate配置信息 --> <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 --> <property name="dialect"> <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 --> org.hibernate.dialect.OracleDialect </property> <!-- Hibernate生成的SQL是否输出到控制台 --> <property name="show_sql">true</property> <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false --> <property name="format_sql">false</property> #cold_bold <!-- 开启二级缓存 --> #cold_bold <property name="hibernate.cache.use_second_level_cache"> #cold_bold true #cold_bold </property> #cold_bold <!-- 指定所采用的二级缓存驱动类 --> #cold_bold <property name="hibernate.cache.provider_class"> #cold_bold org.hibernate.cache.EhCacheProvider #cold_bold </property> <!-- 声明映射关系文件 --> <mapping resource="com/tarena/entity/Emp.hbm.xml" /> <mapping resource="com/tarena/entity/Account.hbm.xml" /> <mapping resource="com/tarena/entity/Service.hbm.xml" /> </session-factory> </hibernate-configuration>
步骤四:设置缓存策略
在Emp.hbm.xml中,指定二级缓存策略为只读的,代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 配置实体类和表的关系 --> <class name="com.tarena.entity.Emp" table="emp"> #cold_bold <!-- #cold_bold 开启二级缓存,指定二级缓存策略。 #cold_bold 可以用region属性指定自定义的缓存设置。 #cold_bold --> #cold_bold <cache usage="read-only" /> <!-- 配置主键属性和字段的关系 --> <id name="id" type="integer" column="id"> <!-- 用来指明主键的生成方式 --> <generator class="sequence"> <!-- 指定用于生成主键的sequence --> <param name="sequence">emp_seq</param> </generator> </id> <!-- 配置实体类中属性与表中字段的关系 --> <property name="name" type="string" column="name"/> <property name="age" type="integer" column="age"/> <property name="salary" type="double" column="salary"/> <property name="birthday" type="date" column="birthday"/> <property name="lastLoginTime" type="timestamp" column="last_login_time"/> <property name="marry" type="yes_no" column="marry"/> </class> </hibernate-mapping>
步骤五:编写查询方法,测试二级缓存
在com.tarena.test包下,创建一个测试类TestSecondCache,增加一个测试二级缓存的方法,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } }
步骤六:测试
执行test1(),控制台输出结果如下图,可见第二次查询没有访问数据库,是二级缓存存在的缘故:
图-23
步骤七:管理二级缓存
二级缓存是SessionFactory级缓存,由它负责管理,因此需要获取到SessionFactory才能管理二级缓存,我们先在HibernateUtil中增加获取SessionFactory的方法,代码如下:
package com.tarena.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static SessionFactory sessionFactory; static { // 加载Hibernate主配置文件 Configuration conf = new Configuration(); conf.configure("/hibernate.cfg.xml"); sessionFactory = conf.buildSessionFactory(); } /** * 创建session */ public static Session getSession() { return sessionFactory.openSession(); } #cold_bold /** #cold_bold * 返回SessionFactory #cold_bold */ #cold_bold public static SessionFactory getSessionFactory() { #cold_bold return sessionFactory; #cold_bold } public static void main(String[] args) { System.out.println(getSession()); } }
修改test1()方法,在第二次查询前清理二级缓存,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); #cold_bold HibernateUtil.getSessionFactory().evict(Emp.class); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } }
再次执行test1(),控制台输出结果如下图,由于清理了二级缓存,因此第二次查询时访问了数据库。
图-24
以下是本案例的完整代码。
其中缓存配置文件ehcache.xml完整代码如下:
<ehcache> <!-- 缓存到硬盘时的缓存路径,java.io.tmpdir表示 系统默认缓存路径。 --> <diskStore path="java.io.tmpdir"/> <!-- 默认缓存配置。 maxElementsInMemory: 二级缓存可容纳最大对象数。 eternal: 是否保持二级缓存中对象不变。 timeToIdleSeconds: 允许对象空闲的时间,即对象最后一次访问起,超过该时间即失效。 timeToLiveSeconds: 允许对象存活的时间,即对象创建起,超过该时间即失效。 overflowToDisk: 内存不足时,是否允许使用硬盘缓存,写入路径参见diskStore。 --> <defaultCache maxElementsInMemory="300" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true" /> <!-- 自定义缓存配置 --> <cache name="myCache" maxElementsInMemory="2000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>
hibernate.cfg.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息,根据自己的数据库进行配置 --> <property name="connection.url"> jdbc:oracle:thin:@localhost:1521:xe </property> <property name="connection.username">lhh</property> <property name="connection.password">123456</property> <property name="connection.driver_class"> oracle.jdbc.OracleDriver </property> <!-- Hibernate配置信息 --> <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 --> <property name="dialect"> <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 --> org.hibernate.dialect.OracleDialect </property> <!-- Hibernate生成的SQL是否输出到控制台 --> <property name="show_sql">true</property> <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false --> <property name="format_sql">false</property> <!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache"> true </property> <!-- 指定所采用的二级缓存驱动类 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- 声明映射关系文件 --> <mapping resource="com/tarena/entity/Emp.hbm.xml" /> <mapping resource="com/tarena/entity/Account.hbm.xml" /> <mapping resource="com/tarena/entity/Service.hbm.xml" /> </session-factory> </hibernate-configuration>
emp.hbm.xml完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 配置实体类和表的关系 --> <class name="com.tarena.entity.Emp" table="emp"> <!-- 开启二级缓存,指定二级缓存策略。 可以用region属性指定自定义的缓存设置。 --> <cache usage="read-only" /> <!-- 配置主键属性和字段的关系 --> <id name="id" type="integer" column="id"> <!-- 用来指明主键的生成方式 --> <generator class="sequence"> <!-- 指定用于生成主键的sequence --> <param name="sequence">emp_seq</param> </generator> </id> <!-- 配置实体类中属性与表中字段的关系 --> <property name="name" type="string" column="name"/> <property name="age" type="integer" column="age"/> <property name="salary" type="double" column="salary"/> <property name="birthday" type="date" column="birthday"/> <property name="lastLoginTime" type="timestamp" column="last_login_time"/> <property name="marry" type="yes_no" column="marry"/> </class> </hibernate-mapping>
TestSecondCache完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); HibernateUtil.getSessionFactory().evict(Emp.class); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } }
在查询多条员工数据时,使用查询缓存,缓存查询的HQL,使得再次使用同样HQL查询时不必重新访问数据库。
查询缓存的使用步骤是
实现此案例需要按照如下步骤进行。
步骤一:开启二级缓存
开启二级缓存,由于上个案例已经完成,这一步就可以省略。但是要注意,查询缓存是基于二级缓存的,使用查询缓存的前提是开启二级缓存。
步骤二:开启查询缓存
在hibernate.cfg.xml中开启查询缓存,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息,根据自己的数据库进行配置 --> <property name="connection.url"> jdbc:oracle:thin:@localhost:1521:xe </property> <property name="connection.username">lhh</property> <property name="connection.password">123456</property> <property name="connection.driver_class"> oracle.jdbc.OracleDriver </property> <!-- Hibernate配置信息 --> <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 --> <property name="dialect"> <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 --> org.hibernate.dialect.OracleDialect </property> <!-- Hibernate生成的SQL是否输出到控制台 --> <property name="show_sql">true</property> <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false --> <property name="format_sql">false</property> <!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache"> true </property> <!-- 指定所采用的二级缓存驱动 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> #cold_bold <!-- 开启查询缓存 --> #cold_bold <property name="hibernate.cache.use_query_cache"> #cold_bold true #cold_bold </property> <!-- 声明映射关系文件 --> <mapping resource="com/tarena/entity/Emp.hbm.xml" /> <mapping resource="com/tarena/entity/Account.hbm.xml" /> <mapping resource="com/tarena/entity/Service.hbm.xml" /> </session-factory> </hibernate-configuration>
步骤三:查询前开启查询缓存
在TestSecondCache中,增加测试查询缓存的方法,使用相同的HQL执行2次查询,每次查询前都设置开启查询缓存,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); HibernateUtil.getSessionFactory().evict(Emp.class); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } #cold_bold /** #cold_bold * 查询缓存 #cold_bold */ #cold_bold @Test #cold_bold public void test2() { #cold_bold Session session = HibernateUtil.getSession(); #cold_bold #cold_bold String hql = "from Emp"; #cold_bold Query query = session.createQuery(hql); #cold_bold // 开启查询缓存 #cold_bold query.setCacheable(true); #cold_bold List<Emp> emps = query.list(); #cold_bold for(Emp e : emps) { #cold_bold System.out.println(e.getId() + " " + e.getName()); #cold_bold } #cold_bold #cold_bold System.out.println("---------------"); #cold_bold #cold_bold hql = "from Emp"; #cold_bold query = session.createQuery(hql); #cold_bold // 开启查询缓存 #cold_bold query.setCacheable(true); #cold_bold emps = query.list(); #cold_bold for(Emp e : emps) { #cold_bold System.out.println(e.getId() + " " + e.getName()); #cold_bold } #cold_bold #cold_bold session.close(); #cold_bold } }
步骤四:测试
执行test2(),控制台输出结果如下图,可见在使用相同HQL查询时,第二次查询不必再次访问数据库。
图-25
步骤五:管理查询缓存
修改test2(),在第二次查询之前清理查询缓存,代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); HibernateUtil.getSessionFactory().evict(Emp.class); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } /** * 查询缓存 */ @Test public void test2() { Session session = HibernateUtil.getSession(); String hql = "from Emp"; Query query = session.createQuery(hql); // 开启查询缓存 query.setCacheable(true); List<Emp> emps = query.list(); for(Emp e : emps) { System.out.println(e.getId() + " " + e.getName()); } System.out.println("---------------"); #cold_bold HibernateUtil.getSessionFactory().evictQueries(); hql = "from Emp"; query = session.createQuery(hql); // 开启查询缓存 query.setCacheable(true); emps = query.list(); for(Emp e : emps) { System.out.println(e.getId() + " " + e.getName()); } session.close(); } }
再次执行test2(),控制台输出结果如下图,可见第二次查询也访问了数据库,主要是清理了查询缓存中数据的缘故。
图-26
以下为本案例的完整代码。
其中Hibernate.cfg.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息,根据自己的数据库进行配置 --> <property name="connection.url"> jdbc:oracle:thin:@localhost:1521:xe </property> <property name="connection.username">lhh</property> <property name="connection.password">123456</property> <property name="connection.driver_class"> oracle.jdbc.OracleDriver </property> <!-- Hibernate配置信息 --> <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 --> <property name="dialect"> <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 --> org.hibernate.dialect.OracleDialect </property> <!-- Hibernate生成的SQL是否输出到控制台 --> <property name="show_sql">true</property> <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false --> <property name="format_sql">false</property> <!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache"> true </property> <!-- 指定所采用的二级缓存驱动 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- 开启查询缓存 --> <property name="hibernate.cache.use_query_cache"> true </property> <!-- 声明映射关系文件 --> <mapping resource="com/tarena/entity/Emp.hbm.xml" /> <mapping resource="com/tarena/entity/Account.hbm.xml" /> <mapping resource="com/tarena/entity/Service.hbm.xml" /> </session-factory> </hibernate-configuration>
TestSecondCache完整代码如下:
package com.tarena.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.tarena.entity.Emp; import com.tarena.util.HibernateUtil; public class TestSecondCache { /** * 二级缓存 */ @Test public void test1() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 321); System.out.println(e1.getName()); System.out.println("-----------------"); HibernateUtil.getSessionFactory().evict(Emp.class); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 321); System.out.println(e2.getName()); session1.close(); session2.close(); } /** * 查询缓存 */ @Test public void test2() { Session session = HibernateUtil.getSession(); String hql = "from Emp"; Query query = session.createQuery(hql); // 开启查询缓存 query.setCacheable(true); List<Emp> emps = query.list(); for(Emp e : emps) { System.out.println(e.getId() + " " + e.getName()); } System.out.println("---------------"); HibernateUtil.getSessionFactory().evictQueries(); hql = "from Emp"; query = session.createQuery(hql); // 开启查询缓存 query.setCacheable(true); emps = query.list(); for(Emp e : emps) { System.out.println(e.getId() + " " + e.getName()); } session.close(); } }