在这里,将创建一个简化的用户管理模块,演示怎样利用SpringSide提供的数据持久层的功能,包括怎样通过Hibernate的Annotation来配置多对一映射和多对多映射。 大家都知道,现在最流行用户管理模型的是RBAC,也就是基于角色的访问控制模型,在这种模型中,可以划分多个层次,如用户-角色-资源、用户-角色-权限-资源、用户-角色-角色组-权限-资源、用户-角色-角色组-权限-操作-资源等等,因此,想要创建一个完善而复杂的用户管理模块,是相当具有难度的。在Web2.0时代,有一个很重要的开发思想,那就是先让程序跑起来,以后再逐步添加复杂的功能。因此,在这里只创建一个简化的用户管理模块。 所谓简化,它具有如下几个特点: 1.在认证方式中,选择基于用户名和密码的认证,用户需要提供用户名、密码和昵称,用户名和昵称都要求不能重复,用户名不能包含中文,且不能够被修改,昵称可以为中文,也可以被修改。密码使用MD5加密。 2.不包含用户的真实信息,如姓名、年龄、性别、职业、地址、邮编等等,因为如果包含这些字段,那么还需要包含更多的额外字段来让用户决定是否公开这些信息,因此,去掉这些东西,可以简化开发过程,让网站能够尽快的跑起来。 3.联系方式只需要用户提供它的电子邮箱和QQ号码。 4.如果用户密码丢失,可以通过密码提示问题找回,随机产生的新密码会发到用户的电子邮箱。 5.省略用户的个性化设置,如个性化签名、自定义头像等。 6.要能够记录用户的注册时间和最后登录时间。 7.要具有完善的积分和排名机制。 8.用户删除的时候不做物理删除,只标记为该用户不可用。 8.具有简化的角色和权限管理机制,这里的简化主要有以下几点:每个用户只能属于一个角色,即多对一关系,而不是传统的多对多关系;角色不需要分组;没有专门的资源抽象层;在角色表中只使用一个字段来表示该角色具有的权限,权限以数字表示,以逗号分开,如“1,2”,“1,3,15”等等。 9.用户可以创建群和加入群,为了简化,群的创始人即为管理员,并不可改变,用户加入群需要管理员批准,一个用户可以加如多个群,即多对多关系。 从上面的描述可以看出,一个简化的用户管理系统最少需要三个表,即users,roles和groups表,其中users和roles之间为多对一映射,users和groups之间为多对多映射,为了实现多对多映射,并且用户加入群的时候需要管理员批准,需要一个中间表users_groups。下面是在MySQL中创建数据表的语句。 创建用户表: 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));
创建角色表: 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));
创建用户群组多对多映射辅助表: 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) );
设计完数据库,就该设计领域对象了,领域对象的设计方法为先设计简单的POJO,然后再在POJO上添加Hibernate Annotation来配置映射关系。在进行Annotation配置的时候,可以从以下几个方面进行思考。 1、使用什么样的数据类型映射数据库中的列类型? 首先来看看users和roles之间的关系,考虑到加载一个用户数据的时候,往往同时需要知道他属于哪个角色,而加载一个角色的时候,就没有必要知道它管理哪些用户了,因此,它们是简单的单向关系,是多对一映射。当出现多对一映射的时候,永远都应该选择多的这一方作为主控方,道理很简单,打个比方,让一个国家元首记住全国人民的名字基本是不可能的,而让全国人民记住国家元首的名字就很简单了。因此,这里User作为主控方,Role作为被控方。 再来看看数据类型的映射,对于简单的int、varchar这样的就不用多说了。而日期时间类型的映射是一个重点,可以看到,前面的数据库创建语句中,所有需要时间的地方都使用了timestamp列类型,使用timestamp列类型的唯一目的就是为了能够使用default CURRENT_TIMESTAMP语句,使用date和datetime类型就不行,在MySQL中,timestamp只能表示从‘1970-01-01 00:00:00‘到2037年的范围。 MySQL中的timestamp和java.sql.Timestamp表现不一致,在MySQL中,timestamp和datetime类型精度是一样的,都只能储存到整数秒,而timestamp比datetime能表示的时间范围要小得多,在Java中,java.util.Date和MySQL的timestamp的精度是一致的,只能储存到整数秒,而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对象的时候就会出错,所以在创建数据库的时候要选择一个合法的默认时间,如‘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做一下注释: 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; } }
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; } }
好了,该开始测试了,看看经过前面设计和配置的代码能否正常工作。首先,先创建三个Manager,这三个Manager都继承自org.springside.core.dao.extend.HibernateEntityExtendDao,至于HibernateEntityExtendDao的功能,请参考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; // 以下代码的作用是注入三个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建立关联 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 " ); } }
<!-- 基本事务定义,使用transactionManager作事务管理,默认get * 方法的事务为readonly,其余方法按默认设置.
默认的设置请参考Spring文档事务一章. --> < 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 > |
|