在這里,將創(chuàng)建一個簡化的用戶管理模塊,演示怎樣利用SpringSide提供的數(shù)據(jù)持久層的功能,包括怎樣通過Hibernate的Annotation來配置多對一映射和多對多映射。
大家都知道,現(xiàn)在最流行用戶管理模型的是RBAC,也就是基于角色的訪問控制模型,在這種模型中,可以劃分多個層次,如用戶-角色-資源、用戶-角色-權(quán)限-資源、用戶-角色-角色組-權(quán)限-資源、用戶-角色-角色組-權(quán)限-操作-資源等等,因此,想要創(chuàng)建一個完善而復(fù)雜的用戶管理模塊,是相當(dāng)具有難度的。在Web2.0時代,有一個很重要的開發(fā)思想,那就是先讓程序跑起來,以后再逐步添加復(fù)雜的功能。因此,在這里只創(chuàng)建一個簡化的用戶管理模塊。
所謂簡化,它具有如下幾個特點:
1.在認(rèn)證方式中,選擇基于用戶名和密碼的認(rèn)證,用戶需要提供用戶名、密碼和昵稱,用戶名和昵稱都要求不能重復(fù),用戶名不能包含中文,且不能夠被修改,昵稱可以為中文,也可以被修改。密碼使用MD5加密。
2.不包含用戶的真實信息,如姓名、年齡、性別、職業(yè)、地址、郵編等等,因為如果包含這些字段,那么還需要包含更多的額外字段來讓用戶決定是否公開這些信息,因此,去掉這些東西,可以簡化開發(fā)過程,讓網(wǎng)站能夠盡快的跑起來。
3.聯(lián)系方式只需要用戶提供它的電子郵箱和QQ號碼。
4.如果用戶密碼丟失,可以通過密碼提示問題找回,隨機(jī)產(chǎn)生的新密碼會發(fā)到用戶的電子郵箱。
5.省略用戶的個性化設(shè)置,如個性化簽名、自定義頭像等。
6.要能夠記錄用戶的注冊時間和最后登錄時間。
7.要具有完善的積分和排名機(jī)制。
8.用戶刪除的時候不做物理刪除,只標(biāo)記為該用戶不可用。
8.具有簡化的角色和權(quán)限管理機(jī)制,這里的簡化主要有以下幾點:每個用戶只能屬于一個角色,即多對一關(guān)系,而不是傳統(tǒng)的多對多關(guān)系;角色不需要分組;沒有專門的資源抽象層;在角色表中只使用一個字段來表示該角色具有的權(quán)限,權(quán)限以數(shù)字表示,以逗號分開,如“1,2”,“1,3,15”等等。
9.用戶可以創(chuàng)建群和加入群,為了簡化,群的創(chuàng)始人即為管理員,并不可改變,用戶加入群需要管理員批準(zhǔn),一個用戶可以加如多個群,即多對多關(guān)系。
從上面的描述可以看出,一個簡化的用戶管理系統(tǒng)最少需要三個表,即users,roles和groups表,其中users和roles之間為多對一映射,users和groups之間為多對多映射,為了實現(xiàn)多對多映射,并且用戶加入群的時候需要管理員批準(zhǔn),需要一個中間表users_groups。下面是在MySQL中創(chuàng)建數(shù)據(jù)表的語句。
創(chuàng)建用戶表:
create
?
table
?users(
id?
int
?
not
?
null
?auto_increment?
primary
?
key
,
name?
varchar
(
20
)?
not
?
null
,
password?
char
(
32
)?
not
?
null
,
monicker?
varchar
(
30
)?
not
?
null
,
question?
varchar
(
30
)?
not
?
null
,
answer?
varchar
(
30
)?
not
?
null
,
email?
varchar
(
40
)?
not
?
null
,
qq?
varchar
(
12
)?
not
?
null
,
roleid?
int
?
not
?
null
,
score?
int
?
not
?
null
?
default
?
'
0
'
,
regtime?
timestamp
?
not
?
null
?
default
?
CURRENT_TIMESTAMP
,
logintime?
timestamp
?
not
?
null
?
default
?
'
2007-01-01?00:00:00
'
,
isdeleted?
varchar
(
2
)?
not
?
null
?
default
?
'
0
'
,
index
(username),
index
(monicker));
為了加快查找用戶的速度,在用戶名和昵稱列上創(chuàng)建了索引。
創(chuàng)建角色表:
create
?
table
?roles(
id?
int
?
not
?
null
?auto_increment?
primary
?
key
,
name?
varchar
(
20
)?
not
?
null
,
privilegesFlag?
varchar
(
255
),
index
(rolename)
);
創(chuàng)建群組表:
create
?
table
?groups(
id?
int
?
not
?
null
?auto_increment?
primary
?
key
,
name?
varchar
(
40
)?
not
?
null
,
creatorid?
int
?
not
?
null
,
createtime?
timestamp
?
not
?
null
?
default
?
CURRENT_TIMESTAMP
,
isdeleted?
varchar
(
2
)?
not
?
null
?
default
?
'
0
'
,
index
(groupname));
creatorid代表組的創(chuàng)始人,同時也是管理員,這里同樣設(shè)置群組不做物理刪除。
創(chuàng)建用戶群組多對多映射輔助表:
create
?
table
?users_groups(
id?
int
?
not
?
null
?auto_increment?
primary
?
key
,
userid?
int
?
not
?
null
,
groupid?
int
?
not
?
null
,
jointime?
timestamp
,
status?
tinyint
,
index
(userid),
index
(groupid)
);
其中status列代表用戶是否通過了管理員的批準(zhǔn),為了加快查找速度,在userid和groupid列上建立索引。
設(shè)計完數(shù)據(jù)庫,就該設(shè)計領(lǐng)域?qū)ο罅耍I(lǐng)域?qū)ο蟮脑O(shè)計方法為先設(shè)計簡單的POJO,然后再在POJO上添加Hibernate Annotation來配置映射關(guān)系。在進(jìn)行Annotation配置的時候,可以從以下幾個方面進(jìn)行思考。
1、使用什么樣的數(shù)據(jù)類型映射數(shù)據(jù)庫中的列類型?
2、對象之間是一對一、一對多還是多對多關(guān)系?
3、關(guān)聯(lián)的對象之間哪一個作為主控方?
4、對象之間的關(guān)聯(lián)是單向的還是雙向的?
首先來看看users和roles之間的關(guān)系,考慮到加載一個用戶數(shù)據(jù)的時候,往往同時需要知道他屬于哪個角色,而加載一個角色的時候,就沒有必要知道它管理哪些用戶了,因此,它們是簡單的單向關(guān)系,是多對一映射。當(dāng)出現(xiàn)多對一映射的時候,永遠(yuǎn)都應(yīng)該選擇多的這一方作為主控方,道理很簡單,打個比方,讓一個國家元首記住全國人民的名字基本是不可能的,而讓全國人民記住國家元首的名字就很簡單了。因此,這里User作為主控方,Role作為被控方。
再來看看數(shù)據(jù)類型的映射,對于簡單的int、varchar這樣的就不用多說了。而日期時間類型的映射是一個重點,可以看到,前面的數(shù)據(jù)庫創(chuàng)建語句中,所有需要時間的地方都使用了timestamp列類型,使用timestamp列類型的唯一目的就是為了能夠使用default CURRENT_TIMESTAMP語句,使用date和datetime類型就不行,在MySQL中,timestamp只能表示從'1970-01-01 00:00:00'到2037年的范圍。
MySQL中的timestamp和java.sql.Timestamp表現(xiàn)不一致,在MySQL中,timestamp和datetime類型精度是一樣的,都只能儲存到整數(shù)秒,而timestamp比datetime能表示的時間范圍要小得多,在Java中,java.util.Date和MySQL的timestamp的精度是一致的,只能儲存到整數(shù)秒,而java.sql.Timestamp還保存毫微秒,因此建議使用java.util.Date來映射timestamp列,使用java.sql.Timestamp只是浪費。
MySQL和Java在時間上面還有一個沖突,那就是MySQL支持全零的時間,如'0000-00-00 00:00:00',而Java不支持,因此如果在定義users表的logintime列時使用logintime timestamp not null default '0000-00-00 00:00:00',那么在使用Hibernate來獲取User對象的時候就會出錯,所以在創(chuàng)建數(shù)據(jù)庫的時候要選擇一個合法的默認(rèn)時間,如'2007-01-01 00:00:00'。
下面請看User.java的代碼:
package
?com.xkland.domain;

import
?java.io.Serializable;
import
?java.util.Date;
import
?org.springside.core.dao.extend.Undeletable;
import
?org.hibernate.annotations.Cache;
import
?org.hibernate.annotations.CacheConcurrencyStrategy;
import
?javax.persistence.
*
;

@Entity
@Table(name
=
"
users
"
)
@Undeletable(status
=
"
isDeleted
"
)

public
?
class
?User?
implements
?Serializable?
{
?
private
?Integer?id;
?
private
?String?name;
?
private
?String?password;
?
private
?String?monicker;
?
private
?String?question;
?
private
?String?answer;
?
private
?String?email;
?
private
?String?qq;
?
private
?Role?role;
?
private
?Integer?score;
?
private
?Date?regTime;
?
private
?Date?loginTime;
?
private
?Byte?isDeleted;
?
?@Id
?@GeneratedValue(strategy?
=
?GenerationType.AUTO)

?
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
?String?getPassword()?
{
??
return
?password;
?}
?
public
?
void
?setPassword(String?password)?
{
??
this
.password?
=
?password;
?}
?

?
public
?String?getMonicker()?
{
??
return
?monicker;
?}
?
public
?
void
?setMonicker(String?monicker)?
{
??
this
.monicker?
=
?monicker;
?}
?

?
public
?String?getQuestion()?
{
??
return
?question;
?}
?
public
?
void
?setQuestion(String?question)?
{
??
this
.question?
=
?question;
?}
?

?
public
?String?getAnswer()?
{
??
return
?answer;
?}
?
public
?
void
?setAnswer(String?answer)?
{
??
this
.answer?
=
?answer;
?}
?

?
public
?String?getEmail()?
{
??
return
?email;
?}
?
public
?
void
?setEmail(String?email)?
{
??
this
.email?
=
?email;
?}
?

?
public
?String?getQq()?
{
??
return
?qq;
?}
?
public
?
void
?setQq(String?qq)?
{
??
this
.qq?
=
?qq;
?}
?
?@ManyToOne
?@JoinColumn(name
=
"
roleid
"
)

?
public
?Role?getRole()?
{
??
return
?role;
?}
?
public
?
void
?setRole(Role?role)?
{
??
this
.role?
=
?role;
?}
?
?@Column(name
=
"
score
"
,insertable
=
false
)

?
public
?Integer?getScore()?
{
??
return
?score;
?}
?
public
?
void
?setScore(Integer?score)?
{
??
this
.score?
=
?score;
?}
?
?@Column(name?
=
?
"
regtime
"
,insertable
=
false
)
?@Temporal(TemporalType.TIMESTAMP)

?
public
?Date?getRegTime()?
{
??
return
?regTime;
?}
?
public
?
void
?setRegTime(Date?regTime)?
{
??
this
.regTime?
=
?regTime;
?}
?
?@Column(name?
=
?
"
logintime
"
,insertable
=
false
)
?@Temporal(TemporalType.TIMESTAMP)

?
public
?Date?getLoginTime()?
{
??
return
?loginTime;
?}
?
public
?
void
?setLoginTime(Date?loginTime)?
{
??
this
.loginTime?
=
?loginTime;
?}
?
?@Column(name?
=
?
"
isdeleted
"
,insertable
=
false
)

?
public
?Byte?getIsDeleted()?
{
??
return
?isDeleted;
?}
?
public
?
void
?setIsDeleted(Byte?isDeleted)?
{
??
this
.isDeleted?
=
?isDeleted;
?}
?
}
這里只對幾個特殊的Annotation做一下注釋:
1、因為創(chuàng)建數(shù)據(jù)表的時候使用的是users,而實體類為User,單復(fù)數(shù)不同引發(fā)名稱不一致,因此需要@Table(name="users");
2、因為該表中的數(shù)據(jù)不做物理刪除,所以加上@Undeletable(status="isDeleted"),結(jié)合SpringSide提供的HibernateEntityExtendDao類,可以在調(diào)用remove方法的時候?qū)sdeleted列設(shè)置為"-1";
3、創(chuàng)建數(shù)據(jù)表的時候,所有的列名都是用的小寫字母,因此有的列映射需要明確指定,如@Column(name = "logintime",insertable=false);
4、對于在創(chuàng)建數(shù)據(jù)表的時候定義了默認(rèn)值的列,如regtime、regtime、logintime、isdeleted,在向數(shù)據(jù)庫中添加數(shù)據(jù)的時候,可以不在insert語句中指定這些列,而讓它們使用默認(rèn)值,因此,需要告訴Hibernate在生成insert語句的時候不要包含這些列,可以使用insertable=false語句,如@Column(name = "regtime",insertable=false);
5、指定時間精度,使用@Temporal(TemporalType.TIMESTAMP);
6、指定users表通過roleid和roles表進(jìn)行多對一映射,使用@ManyToOne和@JoinColumn(name="roleid")
Role.java則比較簡單,如下:
package
?com.xkland.domain;

import
?java.io.Serializable;

import
?javax.persistence.Entity;
import
?javax.persistence.GeneratedValue;
import
?javax.persistence.GenerationType;
import
?javax.persistence.Id;
import
?javax.persistence.Table;

@Entity
@Table(name
=
"
roles
"
)

public
?
class
?Role?
implements
?Serializable?
{
?
private
?Integer?id;
?
private
?String?name;
?
private
?String?privilegesFlag;
?
?@Id
?@GeneratedValue(strategy?
=
?GenerationType.AUTO)

?
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
?String?getPrivilegesFlag()?
{
??
return
?privilegesFlag;
?}
?
public
?
void
?setPrivilegesFlag(String?privilegesFlag)?
{
??
this
.privilegesFlag?
=
?privilegesFlag;
?}
}
下一步再來看看users和groups之間的映射關(guān)系,不難想象,當(dāng)載入一個用戶的資料時,往往需要知道他加入了哪些群,而載入一個群的資料時,往往需要知道它有哪些用戶,因此,他們之間是一個雙向的關(guān)系,同時,載入一個群的資料時,還需要知道它的管理員是誰,因此又同時存在一個單向的多對一關(guān)系。在多對多關(guān)系中,設(shè)定User為主控方,所以需要在User.java中添加如下代碼?
private
?List
<
Group
>
?groups;
@ManyToMany(targetEntity
=
User.
class
,

?cascade
=
{CascadeType.PERSIST,?CascadeType.MERGE}
)
@JoinTable(name
=
"
users_groups
"
,

?joinColumns
=
{@JoinColumn(name
=
"
userid
"
)}
,

?inverseJoinColumns
=
{@JoinColumn(name
=
"
groupid
"
)}
)

public
?List
<
Group
>
?getGroups()?
{
?
return
?groups;
}
public
?
void
?setGroups(List
<
Group
>
?groups)?
{
?
this
.groups?
=
?groups;
}
而整個Group.java的代碼如下:
package
?com.xkland.domain;

import
?java.io.Serializable;
import
?java.util.Date;
import
?java.util.List;
import
?org.springside.core.dao.extend.Undeletable;
import
?org.hibernate.annotations.Cache;
import
?org.hibernate.annotations.CacheConcurrencyStrategy;
import
?javax.persistence.
*
;

@Entity
@Table(name
=
"
groups
"
)
@Undeletable(status
=
"
isDeleted
"
)

public
?
class
?Group?
implements
?Serializable?
{
????
private
?Integer?id;
????
private
?String?name;
????
private
?User?creator;
????
private
?Date?createTime;
????
private
?String?isDeleted;
????
????
private
?List
<
User
>
?users;

????@Id
????@GeneratedValue(strategy?
=
?GenerationType.AUTO)

????
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;
????}
????

????@ManyToOne(cascade
=
{CascadeType.PERSIST,?CascadeType.MERGE}
)
????@JoinColumn(name
=
"
creatorid
"
)

????
public
?User?getCreator()?
{
????????
return
?creator;
????}
????
public
?
void
?setCreator(User?creator)?
{
????????
this
.creator?
=
?creator;
????}
????
????@Column(name
=
"
createtime
"
,insertable
=
false
)
????@Temporal(TemporalType.TIMESTAMP)

????
public
?Date?getCreateTime()?
{
????????
return
?createTime;
????}
????
public
?
void
?setCreateTime(Date?createTime)?
{
????????
this
.createTime?
=
?createTime;
????}
????@Column(name
=
"
isdeleted
"
,insertable
=
false
)

????
public
?String?getIsDeleted()?
{
????????
return
?isDeleted;
????}
????
public
?
void
?setIsDeleted(String?isDeleted)?
{
????????
this
.isDeleted?
=
?isDeleted;
????}
????@ManyToMany(cascade
=
{CascadeType.PERSIST,?CascadeType.MERGE}
,
????????????mappedBy
=
"
groups
"
,
????????????targetEntity
=
User.
class
)

????
public
?List
<
User
>
?getUsers()?
{
????????
return
?users;
????}
????
public
?
void
?setUsers(List
<
User
>
?users)?
{
????????
this
.users?
=
?users;
????}
}
?
好了,該開始測試了,看看經(jīng)過前面設(shè)計和配置的代碼能否正常工作。首先,先創(chuàng)建三個Manager,這三個Manager都繼承自org.springside.core.dao.extend.HibernateEntityExtendDao,至于HibernateEntityExtendDao的功能,請參考SpringSide的文檔。代碼如下:
UserManager.java:
package
?com.xkland.manager;

import
?org.springside.core.dao.extend.HibernateEntityExtendDao;
import
?com.xkland.domain.User;


public
?
class
?UserManager?
extends
?HibernateEntityExtendDao
<
User
>
?
{
?
}
RoleManager.java:
package
?com.xkland.manager;

import
?org.springside.core.dao.extend.HibernateEntityExtendDao;
import
?com.xkland.domain.Role;


public
?
class
?RoleManager?
extends
?HibernateEntityExtendDao
<
Role
>
?
{
?
}
GroupManager.java:
package
?com.xkland.manager;

import
?org.springside.core.dao.extend.HibernateEntityExtendDao;
import
?com.xkland.domain.Group;


public
?
class
?GroupManager?
extends
?HibernateEntityExtendDao
<
Group
>
?
{

}
下一步,將User.class、Role.class、Group.class等領(lǐng)域?qū)ο筇砑拥絪rc\main\resources\config\hibernate.cfg.xml中,如下:
<!
DOCTYPE?hibernate-configuration?PUBLIC
????????"-//Hibernate/Hibernate?Configuration?DTD?3.0//EN"
????????"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
>
<
hibernate-configuration
>
????
<
session-factory
>
????????
<
mapping?
class
="com.xkland.domain.Role"
/>
????????
<
mapping?
class
="com.xkland.domain.User"
/>
????????
<
mapping?
class
="com.xkland.domain.Group"
/>
????
</
session-factory
>
</
hibernate-configuration
>
再下一步,將上面的三個Manager類交給Spring管起來,配置src\main\resources\spring\serviceContext.xml,如下:
<?
xml?version="1.0"?encoding="UTF-8"
?>
<!
DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd"
>
<
beans?
default-lazy-init
="true"
?default-autowire
="byName"
>
????
<
bean?
id
="roleManager"
?class
="com.xkland.manager.RoleManager"
/>
????
<
bean?
id
="userManager"
?class
="com.xkland.manager.UserManager"
/>
????
<
bean?
id
="groupManager"
?class
="com.xkland.manager.GroupManager"
/>
</
beans
>
最后一步,編寫一個Action類,用Spring將上面的三個Manager注入到Action中,測試能否順利的操作數(shù)據(jù)庫。Action類的代碼如下:
package
?com.xkland.action;

import
?org.apache.struts.action.Action;
import
?org.apache.struts.action.ActionForward;
import
?org.apache.struts.action.ActionForm;
import
?org.apache.struts.action.ActionMapping;
import
?javax.servlet.http.HttpServletRequest;
import
?javax.servlet.http.HttpServletResponse;
import
?com.xkland.manager.
*
;
import
?com.xkland.domain.
*
;


public
?
class
?WelcomeAction?
extends
?Action?
{
?
private
?RoleManager?roleManager;
?
private
?UserManager?userManager;
?
private
?GroupManager?groupManager;
?
?
//
以下代碼的作用是注入三個Manager
?
public
?
void
?setUserManager(UserManager?userManager)?
{
??
this
.userManager?
=
?userManager;
?}
?
public
?
void
?setRoleManager(RoleManager?roleManager)?
{
??
this
.roleManager?
=
?roleManager;
?}
?

?
public
?
void
?setGroupManager(GroupManager?groupManager)
{
??
this
.groupManager?
=
?groupManager;
?}
?
?
public
?ActionForward?execute(
???ActionMapping?mapping,
???ActionForm?form,
???HttpServletRequest?request,
???HttpServletResponse?response

???)
{
??
??
//
以下代碼測試能否添加role
??Role?role?
=
?
new
?Role();
??role.setName(
"
第一個角色
"
);
??role.setPrivilegesFlag(
"
1,2,3,4,
"
);
??roleManager.save(role);
??
??
//
以下代碼測試能否添加user
??User?user?
=
?
new
?User();
??user.setAnswer(
"
aa
"
);
??user.setEmail(
"
aa
"
);
??user.setQq(
"
aa
"
);
??user.setName(
"
abcdefg
"
);
??user.setPassword(
"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
"
);
??user.setQuestion(
"
aa
"
);
??user.setMonicker(
"
abcdefg
"
);
??user.setRole(roleManager.get(
1
));
??userManager.save(user);
??
??
//
以下代碼測試能否添加group
??Group?group?
=
?
new
?Group();
??group.setName(
"
第一個用戶組
"
);
??group.setCreator(user);
??groupManager.save(group);
??
??
//
以下代碼測試將user和group建立關(guān)聯(lián)
??user?
=
?userManager.get(
1
);
??group?
=
?groupManager.get(
1
);
??user.getGroups().add(group);
??group.getUsers().add(user);
??userManager.save(user);
??groupManager.save(group);
??
??
//
重定向到
??
return
?
new
?ActionForward(
"
/welcome.jsp
"
);
?}
}
怎樣配置Action這里就不用多嘴了,請參考SpringSide的文檔。這里還要說一句,一定要記得修改src\main\resources\spring\applicationContext.xml中的事務(wù)配置中的package,否則運行會出錯,配置文件片斷如下:
<!--
?以AspectJ方式?定義?AOP?
-->
????
<
aop:config?
proxy-target-class
="true"
>
????????
<!--
?注意,請把第2個*號換為項目package?
-->
????????
<
aop:advisor?
pointcut
="execution(*?*..manager.*Manager.*(..))"
?advice-ref
="txAdvice"
/>
????????
<
aop:advisor?
pointcut
="execution(*?org.springside.core.dao.*Dao.*(..))"
?advice-ref
="txAdvice"
/>
????
</
aop:config
>
????
<!--
?基本事務(wù)定義,使用transactionManager作事務(wù)管理,默認(rèn)get*方法的事務(wù)為readonly,其余方法按默認(rèn)設(shè)置.
?????????????默認(rèn)的設(shè)置請參考Spring文檔事務(wù)一章.?
-->
????
<
tx:advice?
id
="txAdvice"
>
????????
<
tx:attributes
>
????????????
<
tx:method?
name
="get*"
?read-only
="true"
/>
????????????
<
tx:method?
name
="find*"
?read-only
="true"
/>
????????????
<
tx:method?
name
="*"
/>
????????
</
tx:attributes
>
????
</
tx:advice
>
?如果有興趣,還可以把hibernate.show_sql設(shè)置為true,以便觀察Hibernate生成的SQL語句,如下圖:
