1. 程式人生 > >(詳細)Hibernate查詢技術(Query、Session、Criteria),Hibernate的三種狀態,Hibernate集合struts2實現登入功能(二)

(詳細)Hibernate查詢技術(Query、Session、Criteria),Hibernate的三種狀態,Hibernate集合struts2實現登入功能(二)

Hibernate中提供了三種查詢方式:

1)Session的查詢:按主鍵查詢查詢,方法為get或load

2)Query的查詢:使用HQL語句或SQL語句完成查詢

3)Criteria的查詢:通過方法和類中屬性的關係,來設定查詢條件,完成查詢。

Session中get和load方法的區別?

1)  如果沒有查詢到資料,get會返回null,而load則直接提示錯誤。

2)  使用load查詢時,可能會出現以下錯誤,因為load方式使用的是懶漢式載入方法。執行load方法時,不立刻查詢資料庫。當用到查詢出的物件的屬性時,才載入資料。

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

解決這個異常的方法:

1)  不關連線;2)不用load,就使用get就可以了

1.1、Query查詢(重點)

1.1.1HQL語句不只支援查詢功能,還支援修改以及刪除功能

public void doRemove(Integer id) throws Exception {
		// 注意,使用Hibernate刪除時,必須先查詢物件,再刪除.
		// HibernateSessionFactory.getSession().delete(findById(id));
		String hql = "DELETE FROM News AS n WHERE n.id = ?" ;
		Query query = HibernateSessionFactory.getSession().createQuery(hql);
		query.setInteger(0, id);
		query.executeUpdate();
	}
注:使用HQL的刪除可以不需要先查詢,直接刪除,支援一次刪除多條資料
開發中的選擇:1)當刪除一條資料時,直接使用session.delete()就可以,因為簡單,hibernate不在乎那點查詢的效能;

2)批量刪除時,使用Hql形式,這是可以提高效能的方法,因為它中間省去了查詢的步驟。

1.1.2HQL語句修改功能

public void doUpdate(News vo) throws Exception {
		// HibernateSessionFactory.getSession().update(vo);
		String hql = "UPDATE News AS n SET n.title = ?,n.content = ? WHERE n.id = ?" ;
		Query query = HibernateSessionFactory.getSession().createQuery(hql);
		query.setString(0, vo.getTitle());
		// ....其他引數一樣設定
		query.executeUpdate();
	}
開發中的選擇:1)如果是直接的修改功能,肯定選擇session.update()方法;

2)如果是隻修改某一欄位,使用HQL方式。
注:HQL語句不支援新增,但是Query支援新增。

1.1.3針對HQL的查詢功能,也支援寫SELECT,可以通過編寫SELECT,來只查詢物件中某一個或某幾個屬性。

但是對於多種欄位不同型別的查詢返回的,Hibernate中只能是陣列。

例如:

public List testHQL() throws Exception {
		String hql = "SELECT n.id,n.title FROM News AS n";
		Query query = HibernateSessionFactory.getSession().createQuery(hql);
		return query.list();
	}

經過測試,發現當只查詢一個欄位時,直接返回該型別的List集合。

但查詢兩個以上的欄位時,返回的是List<Object[]>,每一條查詢出的資料,使用Object[]來表示,這就很不方便。

public void testHQL() throws Exception {
		List all = ServiceFactory.getINewsServiceInstance().testHQL();
		Object[] value1 = (Object[]) all.get(0);
		System.out.println(value1[1]);
	}

這樣使用起來很麻煩,因此在Hibernate3.2以上的版本中,提供了一個自動轉換類,可以將查詢出的Object[],自動轉換為pojo 物件。

public List testHQL() throws Exception {
		String hql = "SELECT n.id AS id,n.title AS title FROM News AS n";
		Query query = HibernateSessionFactory.getSession().createQuery(hql);
		query
				.setResultTransformer(new AliasToBeanResultTransformer(
						News.class));
		return query.list();
	}
注:一般開發中不會使用這種方法,只有當表中的欄位過多,但查詢只需要其中的幾個欄位時,才會用到這種方法。

1.1.4Hibernate還可以將語句寫到配置檔案中

<query name="findAll">
		FROM News AS n WHERE n.title LIKE ?
	</query>

通過程式讀取配置檔案,取得這段HQL,並生成Query物件,完成查詢。

Query query = HibernateSessionFactory.getSession().getNamedQuery(
				"findAll");
		query.setString(0, "%測試%");
		return query.list();
這種方式在Mybatis中普遍使用,但是在Hibernate中一班很少這樣做。

1.2、Criteria查詢(瞭解)

Criteria也是Hibernate提供的一個查詢物件,支援按物件的方式來完成查詢。例如:

查詢全部功能:

public List<News> testCriteria() throws Exception {
		// 根據傳入的pojo型別,查詢該型別對應的全部資料
		Criteria c = HibernateSessionFactory.getSession().createCriteria(
				News.class);		
		return c.list();
	}

如果想加入查詢條件,需要使用Restrictions的各種方法來完成條件的拼寫。

public List<News> testCriteria() throws Exception {
		// 根據傳入的pojo型別,查詢該型別對應的全部資料
		Criteria c = HibernateSessionFactory.getSession().createCriteria(
				News.class);
		// 1、WHERE id = 26
		// c.add(Restrictions.eq("id", 26));
		// 2、WHERE id > 26
		// c.add(Restrictions.gt("id", 26));
		// 3、WHERE id < 26
		// c.add(Restrictions.lt("id", 26));
		// 4、WHERE id >= 26
		// c.add(Restrictions.ge("id", 26));
		// 5、WHERE id <= 26
		// c.add(Restrictions.le("id", 26));
		// 6、WHERE id <> 26
		// c.add(Restrictions.ne("id", 26));
		// 7、WHERE title LIKE '%測試%'
		// c.add(Restrictions.like("title", "%測試%"));
		// 8、WHERE id between 23 and 27
		// c.add(Restrictions.between("id", 23, 27));
		// 9、WHERE id IN (23,25,27)
		// List<Integer> allIds = new ArrayList<Integer>();
		// allIds.add(23);
		// allIds.add(25);
		// allIds.add(27);
		// c.add(Restrictions.in("id", allIds));
		// 10、複雜條件,需要使用and或or來連線各個條件
		// WHERE id = 23 OR (id <> 26 AND title LIKE '%測試%')
		c
				.add(Restrictions.or(Restrictions.eq("id", 23), Restrictions
						.and(Restrictions.ne("id", 26), Restrictions.like(
								"title", "%測試%"))));
		return c.list();
	}

如果想加入ORDER BY排序條件,需要使用Order物件。

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

如果想加入統計函式和分組函式,則需要用到Projection這個類

ProjectionList pro = Projections.projectionList();
		// 加入統計函式
		pro.add(Projections.rowCount());
		// 還可以加入分組條件
		pro.add(Projections.groupProperty("title"));
		c.setProjection(pro);

、Hibernate中Session操作的三種狀態

Session操作過程中的pojo物件存在三種狀態:

1)  瞬時態:該物件在資料庫中沒有對應的資料。(剛new出來的資料)

2)  持久態:資料庫中存在該物件對應的資料,同時操作該物件的Session也存在。

3)  遊離態:資料庫中包含該物件對應的資料,但操作此物件的Session已經不存在或被關閉了。

三種狀態之間的轉換

瞬時 -->持久:save(),saveOrUpdate()

持久 -->瞬時:delete()

持久 -->遊離:close()

遊離 --> 持久:update(),saveOrUpdate()

針對持久態物件,Hibernate還存在以下兩個特點

1)  持久態物件,在同一Session中只存在同一個。

a)         如果連線不關閉,多次查詢同一條資料,只返回同一個物件,也就是隻查詢一次資料庫。

b)         此功能也被稱為一級快取,但實際開發中實用性很低。

2)  修改持久態物件的屬性,可以自動同步到資料庫對應的資料中。

a)         當修改了一個持久態物件的屬性,而且提交了事務,則資料庫自動呼叫更新操作,也一起修改。

b)        (用處) 當登陸後,要求將當前系統時間,作為最後登陸時間儲存到資料庫中時,可以使用。

三、Struts2 + Hibernate實現使用者登陸功能

3.1先建立專案,根據需要加入框架支援

加入Hibernate:上一部落格中詳細介紹過,略

加入Struts2:右擊專案名,在MyEclipse中找到Add Struts Capabilities;

點選next,只需加入它的核心Core Jar包就可以了;

點選Finish,即加好了struts的核心jar包;

3.2建立一張使用者表

CREATE TABLE T_User (
       userid         varchar2(40)        primary key , 
       real_name      varchar2(20)        not null,
       password       varchar2(32)        not null,
       regist_date    date                default sysdate,
       last_login_date         date                           
);

INSERT INTO t_USER (userid,real_name,password) 
VALUES ('zhangsan','張三','123');

commit;

根據表,在DB Browser下生成對映,主鍵使用assigned方式生成

3.3生成對映後,根據需要,完成DAO的方法和它的實現

DAO方法:

public boolean isLogin(TUser user) throws Exception;

DAOImpl實現:

public class TUserDAOImpl implements ITUserDAO {
	public boolean isLogin(TUser user) throws Exception {
		String hql = "FROM TUser AS u WHERE u.userid = ? AND u.password = ?";
		Query query = HibernateSessionFactory.getSession().createQuery(hql);
		query.setString(0, user.getUserid());
		query.setString(1, user.getPassword());
		List<User> allUser = query.list();
		if (allUser != null && allUser.size() > 0) {
			// 登陸成功
			// 取得查詢的結果
			TUser result = (TUser) allUser.get(0);
			// 根據引用傳遞,將值設定到傳入的user中
			user.setRealName(result.getRealName());
			user.setRegistDate(result.getRegistDate());
			user.setLastLoginDate(result.getLastLoginDate());
			return true;
		}
		return false;
	}
}

再實現Service以及工廠的程式碼。

工廠類:

public class DAOFactory{

public static ITUserDAO getITUserDAOInstance(){

return new TUserDAOImpl();

}

}

3.4後臺程式碼實現後,開始編寫前臺Struts操作程式碼。

匯入Struts2標籤庫,完成表單

<%@ taglib uri="/struts-tags" prefix="s"%>

表單可以使用普通標籤,也可以使用Struts2標籤

<center>
			<form action="tuser!login.action" method="post">
				使用者名稱:<input type="text" name="tuser.userid"/> <br/>
				密碼:<input type="password" name="tuser.password"/> <br/>
				<input type="submit" value="提交" /> 
			</form>
		//下面的為struts標籤
			<s:form action="tuser!login" namespace="/" method="post" theme="simple">
				使用者名稱:<s:textfield name="tuser.userid"></s:textfield> <br/>
				密碼:<s:password name="tuser.password"></s:password> <br/>
				<s:submit value="提交"></s:submit>
			</s:form>
		</center>

編寫Action類,完成驗證的操作。

public class TUserAction extends ActionSupport {
	private TUser tuser;
	public TUser getTuser() {
		return tuser;
	}
	public void setTuser(TUser tuser) {
		this.tuser = tuser;
	}

	public String login() throws Exception {
		boolean flag = ServiceFactory.getITUserServiceInstance().login(tuser);

		// 根據結果,決定跳轉的位置
		if (flag) {
			// 將使用者儲存到session屬性範圍中
			ServletActionContext.getRequest().getSession().setAttribute("user",
					tuser);
			return "suc";
		}
		// 新增錯誤資訊,頁面使用標籤顯示
		super.addActionError("使用者名稱或密碼錯誤,請重新輸入!");
		return "input";
	}
}

配置這個Action,在struts.xml中完成。

<struts>
	<package name="root" namespace="/" extends="struts-default">
		<action name="tuser" class="org.liky.login.action.TUserAction">
			<result name="suc">/pages/suc.jsp</result>
			<result name="input">/index.jsp</result>
		</action>
	</package>
</struts>    

完成suc.jsp中顯示使用者資訊的功能

<center>
			使用者登陸成功,當前登陸使用者為: ${user.userid} 
		</center>

在index.jsp中提示錯誤資訊

<font color="red">
			<s:actionerror/>
		</font>

測試專案,會提示以下錯誤資訊。

java.lang.NoSuchMethodError:antlr.collections.AST.getLine()

這種錯誤是由於專案中加入了多個版本的支援jar包,各個版本之間有相同的類,但方法不同,造成衝突。

這裡的衝突jar包是Struts2加入的2.7.2.jar造成的,需要將該包刪除,該包是MyEclipse工具提供的,因此需要通過MyEclip-se的操作來刪除,無法直接刪除。

windows-->preference-->MyEclipse-->project capabilities-->Struts2中找到MyEclipse提供的antlr-2.7.2jar並刪除。(這個jar包是MyEclipse自動加入的)

刪除後,必須重新發布專案,並重新啟動伺服器才可以。

注:MyEclipse8.6就這兩個衝突;7.5是衝突兩個,少了三個;5.5是衝突一個,缺5個;

最好的做法備份專案Tomcat下的lib中的jar包,包含struts和Hibernate的所有jar包。