在這里,,將創(chuàng)建一個(gè)簡(jiǎn)化的用戶管理模塊,,演示怎樣利用SpringSide提供的數(shù)據(jù)持久層的功能,包括怎樣通過(guò)Hibernate的Annotation來(lái)配置多對(duì)一映射和多對(duì)多映射,。 大家都知道,,現(xiàn)在最流行用戶管理模型的是RBAC,,也就是基于角色的訪問(wèn)控制模型,在這種模型中,,可以劃分多個(gè)層次,,如用戶-角色-資源、用戶-角色-權(quán)限-資源,、用戶-角色-角色組-權(quán)限-資源,、用戶-角色-角色組-權(quán)限-操作-資源等等,因此,,想要?jiǎng)?chuàng)建一個(gè)完善而復(fù)雜的用戶管理模塊,是相當(dāng)具有難度的,。在Web2.0時(shí)代,有一個(gè)很重要的開發(fā)思想,,那就是先讓程序跑起來(lái),以后再逐步添加復(fù)雜的功能。因此,在這里只創(chuàng)建一個(gè)簡(jiǎn)化的用戶管理模塊,。 所謂簡(jiǎn)化,,它具有如下幾個(gè)特點(diǎn): 1.在認(rèn)證方式中,選擇基于用戶名和密碼的認(rèn)證,,用戶需要提供用戶名,、密碼和昵稱,用戶名和昵稱都要求不能重復(fù),,用戶名不能包含中文,,且不能夠被修改,昵稱可以為中文,,也可以被修改,。密碼使用MD5加密。 2.不包含用戶的真實(shí)信息,,如姓名,、年齡、性別,、職業(yè),、地址、郵編等等,,因?yàn)槿绻@些字段,,那么還需要包含更多的額外字段來(lái)讓用戶決定是否公開這些信息,因此,,去掉這些東西,,可以簡(jiǎn)化開發(fā)過(guò)程,讓網(wǎng)站能夠盡快的跑起來(lái),。 3.聯(lián)系方式只需要用戶提供它的電子郵箱和QQ號(hào)碼,。 4.如果用戶密碼丟失,可以通過(guò)密碼提示問(wèn)題找回,,隨機(jī)產(chǎn)生的新密碼會(huì)發(fā)到用戶的電子郵箱,。 5.省略用戶的個(gè)性化設(shè)置,如個(gè)性化簽名,、自定義頭像等,。 6.要能夠記錄用戶的注冊(cè)時(shí)間和最后登錄時(shí)間。 7.要具有完善的積分和排名機(jī)制,。 8.用戶刪除的時(shí)候不做物理刪除,,只標(biāo)記為該用戶不可用。 8.具有簡(jiǎn)化的角色和權(quán)限管理機(jī)制,,這里的簡(jiǎn)化主要有以下幾點(diǎn):每個(gè)用戶只能屬于一個(gè)角色,,即多對(duì)一關(guān)系,,而不是傳統(tǒng)的多對(duì)多關(guān)系;角色不需要分組,;沒(méi)有專門的資源抽象層,;在角色表中只使用一個(gè)字段來(lái)表示該角色具有的權(quán)限,權(quán)限以數(shù)字表示,,以逗號(hào)分開,,如“1,2”,“1,3,15”等等,。 9.用戶可以創(chuàng)建群和加入群,,為了簡(jiǎn)化,群的創(chuàng)始人即為管理員,,并不可改變,,用戶加入群需要管理員批準(zhǔn),一個(gè)用戶可以加如多個(gè)群,,即多對(duì)多關(guān)系,。 從上面的描述可以看出,一個(gè)簡(jiǎn)化的用戶管理系統(tǒng)最少需要三個(gè)表,,即users,,roles和groups表,其中users和roles之間為多對(duì)一映射,,users和groups之間為多對(duì)多映射,,為了實(shí)現(xiàn)多對(duì)多映射,并且用戶加入群的時(shí)候需要管理員批準(zhǔn),,需要一個(gè)中間表users_groups,。下面是在MySQL中創(chuàng)建數(shù)據(jù)表的語(yǔ)句。 創(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)建角色表: create table roles(
id int not null auto_increment primary key , name varchar ( 20 ) not null , privilegesFlag varchar ( 255 ), index (rolename) );
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));
創(chuàng)建用戶群組多對(duì)多映射輔助表: 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) );
設(shè)計(jì)完數(shù)據(jù)庫(kù),就該設(shè)計(jì)領(lǐng)域?qū)ο罅?,領(lǐng)域?qū)ο蟮脑O(shè)計(jì)方法為先設(shè)計(jì)簡(jiǎn)單的POJO,然后再在POJO上添加Hibernate Annotation來(lái)配置映射關(guān)系,。在進(jìn)行Annotation配置的時(shí)候,,可以從以下幾個(gè)方面進(jìn)行思考。 1、使用什么樣的數(shù)據(jù)類型映射數(shù)據(jù)庫(kù)中的列類型,? 首先來(lái)看看users和roles之間的關(guān)系,考慮到加載一個(gè)用戶數(shù)據(jù)的時(shí)候,,往往同時(shí)需要知道他屬于哪個(gè)角色,,而加載一個(gè)角色的時(shí)候,就沒(méi)有必要知道它管理哪些用戶了,,因此,,它們是簡(jiǎn)單的單向關(guān)系,是多對(duì)一映射,。當(dāng)出現(xiàn)多對(duì)一映射的時(shí)候,,永遠(yuǎn)都應(yīng)該選擇多的這一方作為主控方,道理很簡(jiǎn)單,,打個(gè)比方,,讓一個(gè)國(guó)家元首記住全國(guó)人民的名字基本是不可能的,而讓全國(guó)人民記住國(guó)家元首的名字就很簡(jiǎn)單了,。因此,,這里User作為主控方,Role作為被控方,。 再來(lái)看看數(shù)據(jù)類型的映射,,對(duì)于簡(jiǎn)單的int、varchar這樣的就不用多說(shuō)了,。而日期時(shí)間類型的映射是一個(gè)重點(diǎn),,可以看到,前面的數(shù)據(jù)庫(kù)創(chuàng)建語(yǔ)句中,,所有需要時(shí)間的地方都使用了timestamp列類型,,使用timestamp列類型的唯一目的就是為了能夠使用default CURRENT_TIMESTAMP語(yǔ)句,使用date和datetime類型就不行,,在MySQL中,,timestamp只能表示從‘1970-01-01 00:00:00‘到2037年的范圍。 MySQL中的timestamp和java.sql.Timestamp表現(xiàn)不一致,,在MySQL中,,timestamp和datetime類型精度是一樣的,,都只能儲(chǔ)存到整數(shù)秒,而timestamp比datetime能表示的時(shí)間范圍要小得多,,在Java中,,java.util.Date和MySQL的timestamp的精度是一致的,只能儲(chǔ)存到整數(shù)秒,,而java.sql.Timestamp還保存毫微秒,,因此建議使用java.util.Date來(lái)映射timestamp列,使用java.sql.Timestamp只是浪費(fèi),。 MySQL和Java在時(shí)間上面還有一個(gè)沖突,,那就是MySQL支持全零的時(shí)間,如‘0000-00-00 00:00:00‘,,而Java不支持,,因此如果在定義users表的logintime列時(shí)使用logintime timestamp not null default ‘0000-00-00 00:00:00‘,那么在使用Hibernate來(lái)獲取User對(duì)象的時(shí)候就會(huì)出錯(cuò),,所以在創(chuàng)建數(shù)據(jù)庫(kù)的時(shí)候要選擇一個(gè)合法的默認(rèn)時(shí)間,,如‘2007-01-01 00:00:00‘。 下面請(qǐng)看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; } } 這里只對(duì)幾個(gè)特殊的Annotation做一下注釋: Role.java則比較簡(jiǎn)單,,如下: 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; } }
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; }
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; } }
好了,,該開始測(cè)試了,看看經(jīng)過(guò)前面設(shè)計(jì)和配置的代碼能否正常工作,。首先,,先創(chuàng)建三個(gè)Manager,這三個(gè)Manager都繼承自org.springside.core.dao.extend.HibernateEntityExtendDao,,至于HibernateEntityExtendDao的功能,,請(qǐng)參考SpringSide的文檔。代碼如下: package com.xkland.manager;
import org.springside.core.dao.extend.HibernateEntityExtendDao; import com.xkland.domain.User; public class UserManager extends HibernateEntityExtendDao < User > { }
package com.xkland.manager;
import org.springside.core.dao.extend.HibernateEntityExtendDao; import com.xkland.domain.Role; public class RoleManager extends HibernateEntityExtendDao < Role > { }
package com.xkland.manager;
import org.springside.core.dao.extend.HibernateEntityExtendDao; import com.xkland.domain.Group; public class GroupManager extends HibernateEntityExtendDao < Group > { }
<! DOCTYPE hibernate - configuration PUBLIC
" -//Hibernate/Hibernate Configuration DTD 3.0//EN " " http://hibernate./hibernate-configuration-3.0.dtd " > < hibernate - configuration > < session - factory > <!--< mapping class = " org.springside.helloworld.model.User " />--> < mapping class = " com.xkland.domain.Role " /> < mapping class = " com.xkland.domain.User " /> < mapping class = " com.xkland.domain.Group " /> </ session - factory > </ hibernate - configuration >
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
<! DOCTYPE beans PUBLIC " -//SPRING//DTD BEAN 2.0//EN " " http://www./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 >
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; // 以下代碼的作用是注入三個(gè)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 ) { // 以下代碼測(cè)試能否添加role Role role = new Role(); role.setName( " 第一個(gè)角色 " ); role.setPrivilegesFlag( " 1,2,3,4, " ); roleManager.save(role); // 以下代碼測(cè)試能否添加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); // 以下代碼測(cè)試能否添加group Group group = new Group(); group.setName( " 第一個(gè)用戶組 " ); group.setCreator(user); groupManager.save(group); // 以下代碼測(cè)試將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 " ); } }
<!-- 基本事務(wù)定義,使用transactionManager作事務(wù)管理,默認(rèn)get * 方法的事務(wù)為readonly,其余方法按默認(rèn)設(shè)置.
默認(rèn)的設(shè)置請(qǐng)參考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 > |
|