- 浏览: 122031 次
- 性别:
- 来自: 广州
-
文章分类
最新评论
-
bwhzhl:
完全忽悠人
你拿1.44的SVN配置 看到的版本的是
Po ...
(转)Apache2.2和SVN1.4.4搭建svn版本控制平台windows版 -
protti:
你没有说清楚codebehind的原理
其实在以前没有cod ...
struts2 code-behind -
fengzi1:
对天乙社区bbscs8实现的详细分析三 -
leibin2009:
一个字"好". 现在公司正在用这个框架,太 ...
WebWork教程二 -
jason34:
太爽了交个朋友可以吗??
QQ10046417
MSN:kso ...
对天乙社区bbscs8实现的详细分析一(附文档下载)
由于BBSCS8是由数据库设计-bean/hbm.xml-DAO-Service-Web(作者laoer回答)这样的创建过程,因此分析这个系统最好是先查看数据库设计(见http://bbs.laoer.com/main-read-15-ff80808113baa8140113d201333e5274.html下载研究),而我的分析是由Service层开始引出讨论的,所以你需对论坛的常用功能有所体会,知道什么是投票贴,怎么样去用,还要有论坛后台管理使用过等等.如果不知道的话,请先在www.laoer.com处或在自己电脑上本地测试以便先对其功能进行体会,请注意!!!
com.laoer.bbscs.service层下有众多的接口:
AgreeAgainstService,它有三个方法:
AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst) throws BbscsException;
AgreeAgainst findAgreeAgainstByUidPidBid(String userID,String postID,long bid);
void removeOutTime(long time)throws BbscsException;
AgreeAgainstImp为其实现:
首先定义了一个logger,设置好agreeAgainstDAO;getAgreeAgainstDAO及setAgreeAgainstDAO;
public AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst) throws BbscsException{
try{
return this.getAgreeAgainstDAO().saveAgreeAgainst(agreeAgainst);
(注:此处为合写,也可写成:
agreeAgainst=this.getAgreeAgainstDAO().saveAgreeAgainst(agreeAgainst);
return agreeAgainst;我这里啰嗦了一下,以后源代码中会用到,别问我为什么~~~)
}catch(Exception ex) {
logger.error(ex);
throw new BbscsException(ex);
}
}
注意的是getAgreeAgainstDAO返回的是AgreeAgainstDAO来自于com.laoer.bbscs.dao包中的interface;
它有三个接口方法:
public AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst);
public AgreeAgainst findAgreeAgainstByUidPidBid(String userID,String postID,long bid);
public void removeOutTime(long time);
而实现上注入到service方法中的是其实现类:
com.laoer.bbscs.dao.hibernate.AgreeAgainstHibernateDAO,其注入了:
<property name="sessionFactory">
<ref local="sessionFactory"></ref>
</property>
一个sessinFacotry唯一,用于获得可操作的session,让我们看看其实现:
它将两个HQL语句都重构成private static final String 了.一个是LOAD_BY_UID_PID_BID,一个是ROMOVE_OUTTIME,注意其extends HibernateDAOSupport抽象类(得到了org.springframework.orm.hibernate3.support.HibernateDAOSupport的支持哦~)这样我们就可以不用get和set这个sessionFacotry了,另外可能使用this.getHibernateTemplate()来进行实际操作了.
saveorupdate(agreeAgainst);对于查找:
先构造一个Object[] o={postID,userID,new Long(bid)};
List list=this.getHibernateTemplate().find(LOAD_BY_UID_PID_BID,o);
if(l==null||l.isEmpty()){
return null;
}
else{
return (AgreeAgaist) l.get(0);
}
}
最后removeOutTime(final long time) {
getHibernateTemplate().execute(new HibernateCallback(){
public Object doInHibernate(Session session) throws HibernateException, SQLException{
Query query=s.createQuery(REMOVE_OUTTIME);
query.setLong(0,time);
query.executeUpdate();
return null;
}
});
};
}
我们看看hbm.xml文件和bean文件,先是bean(AgreeAgainst.java)
它有如下属性:
private String id;
private String userID;
private String postID;
private long boardID;
private int voteType;
private long createTime;
及其set/get方法的一个构造.
看下AgreeAgainst.hbm.xml文件:
<hibernate-mapping package="com.laoer.bbscs.bean"></hibernate-mapping>
<class name="AgreeAgainst" table="bbscs_agreeagainst"></class>
<id name="id" unsaved-value="null" type="string" column="ID"></id>
<generator class="uuid"></generator>
<property></property>
<property></property>
<property></property>
<property></property>
<property name="createTime" type="long" column="CreateTime" not-null="true"></property>
id为主键uuid算法,还要其长度的定义等等!其它不是......
看下数据库表!!! Null Default
ID varchar(40) NO
UserID varchar(40) No
PostID varchar(40)No
BoardID bigint(20)No 0
VoteType tinyint(1)Yes 0
CreateTime bigint(20)No 0
我们来看下实现:
在帖子的支持和反对处选择!
Hibernate: insert into bbscs_agreeagainst (UserID, PostID, BoardID, VoteType, CreateTime, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select agreeagain0_.ID as ID24_, agreeagain0_.UserID as UserID24_, agreeagain0_.PostID as PostID24_, agreeagain0_.BoardID as BoardID24_, agreeagain0_.VoteType as VoteType24_, agreeagain0_.CreateTime as CreateTime24_ from bbscs_agreeagainst agreeagain0_ where agreeagain0_.PostID=? and agreeagain0_.UserID=? and agreeagain0_.BoardID=?
数据库中的数据:
ID:402881 e513bd c85501 13be01 e70d00 1f(32位)
UserID:4028818208ed006b0108ed020bd50001
PostID:402881e513bdc8550113bdefb43c0014
BoardID:2(第2个建的)
VoteType:1(反对)0(支持)
CreateTime:1184303802125
对比一下,就OK了
BoardAuthUserService(版块授权的用户)
有public BoardAuthUser saveBoardAuthUser(BoardAuthUser boardAuthUser) throws BbscsException;
punlic BoardAuthUser findBoardAuthUserById(String id);
public BoardAuthUser findBoardAuthUserByBidUid(long bid,String uid);
public BoardAuthUser findBoardAuthUserByBidUserName(long bid,String userName);
public List findBoardAuthUserByBid(long bid);
public void removeBoardAuthUser(BoardAuthUser boardAuthUser);
public void removeBoardAuthuserByBidUid(long bid,String uid) throws BbscsException;
public void removeBoardAuthUserByBidUserName(long bid,String userName) throws BbscsExeption;
同样,imp里BoardAuthUserServiceImp中,注入DAO
由DAO来完成对应方法的实际工作.注意:有Exception方法的写法,例如:
public void removeBoardAuthUserByBidUserName(long bid, String userName) throws BbscsException {
try {
this.getBoardAuthUserDAO().removeBoardAuthUserByBidUserName(bid, userName);
}
catch (Exception ex) {
logger.error(ex);
throw new BbscsException(ex);
}
}
来到DAO接口层,没有一个Exception方法哦!!!同样实现它的HibernateDAO也没有Ecxeption,可见,Serivce层有Exception.而DAO层没有Exception(一般情况下),除了没有Exception外,接口层与Serivce接口层好象.说说Exception:这里用到的只不过是一个BbscsException而已,在专门的com.laoer.bbscs.exception包中有这个类:原来它也继承了Exception而已,有三个重载的方法:
public BbscsException(String message) {
super(message);
}
public BbscsException(String message, Throwable cause) {
super(message, cause);
}
public BbscsException(Throwable cause) {
super(cause);
}
自定义异常类的主要作用是区分异常发生的位置,当用户遇到异常时,根据异常名就可以知道哪里有异常,根据异常提示信息进行修改。
看其hibernate实现:
整个源码重构过似的,上面为HQL语句字串常量定义:
private static final String LOAD_BY_BID_UID = "from BoardAuthUser where boardID = ? and userID = ?";
private static final String LOADS_BY_BID = "from BoardAuthUser where boardID = ? order by createTime asc";
看看方法吧:
public BoardAuthUser findBoardAuthUserById(String id) {
return (BoardAuthUser)this.getHibernateTemplate().get(BoardAuthUser.class, id);
}
这个ID明显是对象标识!
public BoardAuthUser findBoardAuthUserByBidUserName(long bid, String userName) {
Object[] o = {new Long(bid), userName};
List l = this.getHibernateTemplate().find(LOAD_BY_BID_USERNAME, o);
if (l == null || l.isEmpty()) {
return null;
}
else {
return (BoardAuthUser) l.get(0);
}
}
public List findBoardAuthUsersByBid(long bid) {
return this.getHibernateTemplate().find(LOADS_BY_BID, new Long(bid));
}
需要注意的是Object数组中的元素必为对象;我们来看看是怎么删除操作的:
public void removeBoardAuthUser(BoardAuthUser boardAuthUser) {
this.getHibernateTemplate().delete(boardAuthUser);
}
而不是一个BoradAuthUser对象,是通过以下代码实现:
public void removeBoardAuthUserByBidUid(final long bid, final String uid) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session s) throws HibernateException, SQLException {
Query query = s.createQuery(REMOVE_BY_BID_UID);
query.setLong(1, bid);
query.setString(0, uid);
query.executeUpdate();
return null;
}
});
}
BoardPermissionService是版区(版块)的权限对应服务;其BEAN BoardPermission如下:
private String id;
private long boardID;
private int groupID;
private List permissions = new ArrayList(); //特殊的哦~~~
再看下其hbm.xml文件:
<hibernate-mapping package="com.laoer.bbscs.bean"></hibernate-mapping>
<class name="BoardPermission" table="bbscs_boardpermission"></class>
<id name="id" unsaved-value="null" type="string" column="ID"></id>
<generator class="uuid"></generator>
<property></property>
<property></property>
<property name="permissions" type="com.laoer.bbscs.ext.hibernate.SplitList" column="Permissions"></property>
注意到:permissions对应于表的Permssions,而其type为com.laoer.bbscs.ext.hibernate.SplitList;
让我们看看SplitList这个类:(它在ext.hibernate包中):
[转自网络:
使用Hibernate自定义UserType遇到的一个问题
Hibernate自定义UserType可以使设计更为优雅,逻辑更为清晰。第一次使用遇到了问题。Rule表中有一个字段methods,类型为VARCHAR(30),not null, 允许有多个method,中间用逗号分开,之所以这么设计是不想为此增加一个关联表。为methods实现了一个自定义UserType叫MethodsList,该类对用户隐藏了实现细节,使用户不用处理method的连接和拆分,而使用List来操作就行了,非常直观。在保存Rule实体的时候,Hibernate报methods字段不允许为空,说明methods在持久化的时候还是null,DEBUG发现在调用session.saveOrUpdate()方法的时候methods不为空,但在调用MethodsList的nullSafeSet(PreparedStatement st, Object value, int index)时显示value的值为null,Google了很久仍然没有找到原因 后来发现只要把methods字段的not null属性设为false(即允许为空)问题就不复存在了,很是奇怪...
据此猜测,Hibernate在应用自定义UserType(即MethodsList)之前进行字段是否为空之类的检查,而这时methods字段还是null,所以出现以上错误,把该字段改为允许为空之后自然就没有问题了。个人愚见,仅供参考,有空研究一下Hibernate源码一探究
robbin:
1、UserType不是用来做主键的(虽然也可以,但是那样和复合主键没有区别了,并且复合主键是非常不推荐的做法)
2、UserType比Component更加灵活,适用性更强,封装的更透明。
]
其中有以下方法:assembledeepCopy(重要)disassmbleequals(重要)hashCodeisMutable ullSafeGet eplace eturnedClasssqlTypes等方法需要实现(不一定)!
rs--->Object
public Object nullSafeGet(ResultSet resultSet, String[] stringArray, Object object) throws HibernateException,
SQLException {
String value = (String) Hibernate.STRING.nullSafeGet(resultSet, stringArray[0]);
if (value != null) {
return parse(value);
} else {
return new ArrayList();
}
}
用了parse方法:
private List parse(String value) {
String[] strs = StringUtils.split(value, SPLITTER);
List set = new ArrayList();
for (int i = 0; i < strs.length; i++) {
if (!StringUtils.isBlank(strs[i])) {
set.add(Long.valueOf(strs[i]));
// System.out.println(strs[i]);
// set.add(new Long(Long.parseLong(strs[i])));
}
}
return set;
}
object--->rs
public void nullSafeSet(PreparedStatement preparedStatement, Object object, int _int) throws HibernateException,
SQLException {
if (object != null) {
String str = assemble((List) object);
Hibernate.STRING.nullSafeSet(preparedStatement, str, _int);
} else {
Hibernate.STRING.nullSafeSet(preparedStatement, "", _int);
}
}
用了assemble方法:
private String assemble(List set) {
StringBuffer sb = new StringBuffer();
Iterator it = set.iterator();
while (it.hasNext()) {
sb.append(it.next());
sb.append(SPLITTER);
}
String fs = sb.toString();
if (fs != null && fs.length() > 0 && fs.endsWith(SPLITTER)) {
fs = fs.substring(0, fs.length() - 1);
}
return fs;
}
附参考资料:http://blog.csdn.net/ckangtai/archive/2007/05/23/1622396.aspx
好,看完这个后,我们进入到服务内容:
public BoardPermission saveBoardPermission(BoardPermission bp) throws BbscsException;
public BoardPermission updateBoardPermission(BoardPermission bp) throws BbscsException;
public BoardPermission findBoardPermissionByID(String id);
public BoardPermission findBoardPermissionByBidGid(long bid, int gid);
public List findBoardPermissionsByBid(long bid);
public List findBoardPermissionsByGid(int gid);
public void removeBoardPermissionsByBid(long bid) throws BbscsException;
public void removeBoardPermissionsByGid(int gid) throws BbscsException;
实际是由注入的boardPermissionDAO其实现类完成的.
注意这里引入了 private Cache userPermissionCache;这个缓存类!
我们从applicationContext.xml看看是什么东东:
<bean id="userPermissionCache"></bean> class="com.laoer.bbscs.service.imp.OsCacheImp">
<constructor-arg></constructor-arg>
<value></value>${cacheup.config}
哦,原来是另外一个服务,${cacheup.config}指的是cacheup.config=oscache_up.properties;
在classes下有许多配置文件,是用不同的配置文件是为了方便集群,以区分是不同的缓存。
我们从com.laoer.bbscs.serivce.Cache接口看起,它提供了如下方法:
public void add(Object key,Object value);
public Object get(Object key);
public void remove(Object key);
public void removeAll();
再看imp:
由于spring中的bean带construtctor-arg:
将调用构造方法:
public OsCacheImp(String profile) {
Properties properties = new Properties();
ClassPathResource classPathResource = new ClassPathResource(profile);
//这个类标识从classpath获得的资源
try {
logger.info("Init Cache...");
properties.load(classPathResource.getInputStream());//使用Properties的load(InputStream inStream) 来读取配置文件的时候
admin = new GeneralCacheAdministrator(properties);
} catch (Exception ex) {
logger.error(ex);
admin = new GeneralCacheAdministrator();
}
}
注意,这个admin对象来自OSCache包,用于管理Cache内容吧.另外,我们看看其对Cache的实现:
public void add(Object key, Object value) {
logger.debug("Add into cache [Key:" + key + "]");
this.admin.putInCache(String.valueOf(key), value);
}
public Object get(Object key) {
try {
logger.debug("Get from cache [Key:" + key + "]");
return this.admin.getFromCache(String.valueOf(key));
} catch (NeedsRefreshException ex) {
logger.debug("Object not in cache, return null");
this.admin.cancelUpdate(String.valueOf(key));
return null;
}
}
public void remove(Object key) {
logger.debug("Remove from cache [Key:" + key + "]");
this.admin.flushEntry(key.toString());
}
public void removeAll() {
logger.debug("Remove all");
this.admin.flushAll();
}
这样就可以对缓存内容进行控制操作了(就用这几个方法)!让我们回到BoardPermissionSerivceImp类中:
注入了DAO和Cache(userPermissionCache)后,进行方法实现时,主要还是DAO去做,不过在saveBoardPermission和updateBoardPermission,还有各种remove方法中还用了this.clearPermissionCache();(除了find),让我们来看看它的代码吧:
private void clearPermissionCache() {
if (Constant.USE_PERMISSION_CACHE) {
//public static final boolean USE_PERMISSION_CACHE = true;
this.getUserPermissionCache().removeAll();//调用Cache中的方法哦!removeALL看上面this.admin.flushAll();!
}
}
555555,这个服务类也了Cache服务(与别的服务结合了),下面是DAO实现:
private static final String LOAD_BY_BID_GID ="from BoardPermission where boardID = ? and groupID = ?";
private static final String LOADS_BY_BID = "from BoardPermission where boardID = ?";
private static final String LOADS_BY_GID = "from BoardPermission where groupID = ?";
private static final String REMOVE_BY_BID = "delete from BoardPermission where boardID = ?";
private static final String REMOVE_BY_GID = "delete from BoardPermission where groupID = ?";
我们看update:
public BoardPermission updateBoardPermission(BoardPermission bp) {
//System.out.println("update bp");
this.getHibernateTemplate().update(bp);
return bp;
}
再看:
/**
* 根据GroupID删除BoardPermission对象
*
* @param gid int
* @todo Implement this com.laoer.bbscs.dao.BoardPermissionDAO method
*/
public void removeBoardPermissionsByGid(final int gid) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session s) throws HibernateException, SQLException {
Query query = s.createQuery(REMOVE_BY_GID);
query.setLong(0, gid);
query.executeUpdate();
return null;
}
});
}
接下来,是BoardSaveService:
先看bean吧,它实现了implements Serializable...,加入private static final long serialVersionUID = 5390014211916049604L;它有三个属性:
private String id;
private String userID;
private long boardID;
[
Java的JavaBeans. Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。
总之如果在网络的环境下做类传输,应该还是implements Serializable。
]
再看其hbm.xml:
<hibernate-mapping package="com.laoer.bbscs.bean"></hibernate-mapping>
<class name="BoardSave" table="bbscs_boardsave"></class>
<id name="id" unsaved-value="null" type="string" column="ID"></id>
<generator class="uuid"></generator>
<property></property>
<property></property>
从这样便知是用于收藏版区的对象模型啊!那接下来看其接口中的方法:
public BoardSave saveBoardSave(BoardSave boardSave) throws BbscsException;
public BoardSave findBoardSaveById(String id);
public BoardSave findBoardSaveByUidBid(String userId, long bid);
public List findBoardSavesByUid(String userId);
public List findBoardSaveBidsByUid(String userId);//找Bid的List
public void removeBoardSave(BoardSave boardSave) throws BbscsException;
public void removeBoardSaveByUidBid(String userId, long bid) throws BbscsException;
public void removeBoardSaveByBid(long bid) throws BbscsException;
public void removeBoardSaveByBidsUid(String userId, List ids) throws BbscsException;
对于imp调用dao-->daoimp我们直接进入dao的imp中:
其中的常量定义如下:
private static final String LOAD_BY_UID_BID = "from BoardSave where userID = ? and boardID = ?";
private static final String LOADS_BY_USERID = "from BoardSave where userID = ?";
private static final String LOADS_BOARDID_BY_USERID = "select boardID from BoardSave where userID = ?";//很特别哦!(其实也没什么,相对而已)
private static final String REMOVE_BY_UID_BID = "delete from BoardSave where userID = ? and boardID = ?";
private static final String REMOVE_BY_BID = "delete from BoardSave where boardID = ?";
private static final String REMOVE_IN_IDS_BY_UID =
"delete from BoardSave where userID = :userID and boardID in (:ids)";
我们来看方法实现吧.
public BoardSave saveBoardSave(BoardSave boardSave) {
this.getHibernateTemplate().saveOrUpdate(boardSave);
return boardSave;
}
可用于保存,也可以用于更新!
public List findBoardSaveBidsByUid(String userId) {
return this.getHibernateTemplate().find(LOADS_BOARDID_BY_USERID, userId);
//一个参数
public void removeBoardSaveByBidsUid(final String userId, final List ids) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session s) throws HibernateException, SQLException {
Query query = s.createQuery(REMOVE_IN_IDS_BY_UID);
query.setString("userID", userId);
query.setParameterList("ids", ids);
query.executeUpdate();
return null;
}
});
}
OK!
接下来,是BoardService,先看其相关的Bean!它也实现了可序列化操作...它的属性特别多..
很关键哦!
private Long id;//主键
private long parentID;//父级版区ID
private List parentIDs;//所有父级版区ID列表
private List childIDs;
private String boardName;//版区名称
private String explains;(说明)
private String bulletin;(公告)
private String boardPic;//图片
private int useStat;(使用状态)
private int orders;//序
private int needPasswd;//是否需要密码访问
private String passwd;//访问密码
private int level;---->数据库中的BoardLevel//级别
private int boardType;(类型1,2,3)//版区类型
private int allowHTML;//HTML是否支持
private int allowUBB;//UBB...
private int auditPost; (帖子是否需要审核)
private int auditAttach;//附件是否需要审核
private int addUserPostNum;//是否增加用户发贴数
private int isHidden;
private int isAuth;//是否需要论证用户才能访问
private long mainPostNum;//主贴数
private long postNum;//帖子数量
private Map boardMaster=new HashMap();
private Set boardTag=new HashSet();
我们看看Board.hbm.xml,错了,我们应该看看applicationCotext.xml中的内容是不是这个,经查看:
<value></value>com/laoer/bbscs/bean/Board-${datasource.type}.hbm.xml,我用mysql当然是Board-mysql.hbm.xml,其内容主要如下:
<hibernate-mapping package="com.laoer.bbscs.bean"></hibernate-mapping>
<class name="Board" table="bbscs_board"></class>
<id name="id" unsaved-value="null" type="long" column="ID"></id>
<generator class="identity"></generator>//long类型的identity!
<property></property>
<property name="parentIDs" type="com.laoer.bbscs.ext.hibernate.SplitList" column="ParentIDs"></property>
//parentIDs有用了userType
<property name="childIDs" type="com.laoer.bbscs.ext.hibernate.SplitList" column="ChildIDs"></property>
<property></property>
<property name="explains" type="text" column="Explains"></property>
//text类型
<property name="bulletin" type="text" column="Bulletin"></property>
<property></property>
<property></property>
//一个开关的功能
<property></property>
//论坛排序
<property></property>
//开关
<property></property>
<property></property>
<property></property>
<property></property>
<property></property>
<property></property>
<property></property>
//不懂???
<property></property>
//应该是一个开关吧
<property></property>
<property></property>
<property></property>
<property></property>
//关键点,boardMaster和boardTag均为one-to-many类型!
<key column="boardID"></key>
<map-key type="string" column="UserName"></map-key>
<one-to-many class="BoardMaster"></one-to-many>
<key column="boardID"></key>
<one-to-many class="BoardTag"></one-to-many>
看完这些后,我们就知道这个bean是用于版区操作了,让我们看看方法:(先看接口BoardService)
public Board saveBoard(Board board) throws BbscsException;//保存或更新Board对象
public Board createBoard(Board board) throws BbscsException;
public Board updateBoard(Board board, long oldParentID) throws BbscsException;
public Board getBoardByID(long id);
public List findBoardsByParentID(long pid, int useStat, int hidden, int orderType);
public List findBoardsAllTree(long pid, List topList, int useStat, int hidden, int orderType);
public int getNextOrder(long pid);
public int getPostSumNum(int mainorall, int useStat, int hidden);
public void removeBoard(Board board) throws BbscsException;
public Map[] getBoardPermission(long bid, int groupID);
public Map[] getBoardMasterPermission(int roleID);
public boolean isBoardMaster(Board board, String userName);
public List findBoardsInIDs(List ids);
public void removeBoardTag(Board board, String tagID) throws BbscsException;
public List getBoardIDs(List boards);
public void saveBoardsPostNumCount() throws BbscsException;
这里方法有16个左右,注意我们进行除了查询后的其它CURD操作都可能会产生异常.对于具体方法我们需要去了解其实现才能理清其作用.看下服务实现层:(注BoardServiceCacheImp为其实现类,可能是加入了许多缓存的原因吧):
首先仍是logger(这里发现了原来其实在service层和dao层只有这里需要logger,其它地方都没,对于异常也只有service接口和实现层有BbscsException这个异常处理,而对于DAO层的异常则已经由HibernateTemplate完全抛出了,如:
public void saveOrUpdate(final Object entity)
throws DataAccessException
{
execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException
{
checkWriteOperationAllowed(session);
session.saveOrUpdate(entity);
return null;
}
}
, true);
}
)
我们回到正题上,除了logger,还有boardDAO(当然要了)
,userGroupDAO,boardPermissionDAO(讲过的),permissionDAO,roleDAO,forumDAO,forumHistoryDAO,还有一些Cache服务:sysListObjCache,boardCache,userPermissionCache(以前用过),还有一个额外的服务类:sysStatService;对他们进行getter/setter方法一下...
我们分析下sysStatService:(applicationContext.xml) 系统统计服务
<bean id="sysStatService"></bean> class="com.laoer.bbscs.service.imp.SysStatServiceImp"
scope="prototype"> //每次请求都产生一个对象
<property name="userConfig">
<ref bean="userConfig"></ref>
</property>
userConfig指:
<bean id="userConfig"></bean> class="com.laoer.bbscs.service.config.UserConfig">
<property name="safePath">
<value></value>${bbscs.safePath} //安全目录我的指的是D:/safe/
</property>
UserConfig是一个config配置服务,在服务类中首先注入了safePath 这个String对象,它向外提供了String getUserFilePath(String userID)[简单,最终得到一个用户文件Path,见实际目录就可知道其原理]和String getIndexPath()和File getIndexFilePath()以及boolean indexExist()四个对外公共方法.详细看方法实现:
public String getIndexPath() { //好象没用到过
StringBuffer sb = new StringBuffer();
sb.append(this.getSafePath());
sb.append("index/");
return sb.toString();
}
public File getIndexFilePath() {
File indexFilePath = new File(this.getIndexPath());//构造一个File对象
if (!indexFilePath.exists()) {
indexFilePath.mkdirs();
}
return indexFilePath;
}
public boolean indexExist() {
File file = new File(this.getIndexPath() + "segments");
return file.exists();//使用文件是否存在的方法作判断!
}
可见config服务类较简单(没用到logger)!提供一个常用服务!而com.laoer.bbscs.service.imp.SysStatService是怎么用到它的呢?它继承了抽象类SysStatService,在这个抽象类中,有一个东西是私有的:
private long onlineNum = 0;
private long appearTime = 0; 发表时间long类型
private String appearTimeStr = "";
private long allUserNum = 0;
private String lastRegUser = "";
private long postMainNum = 0;
private long postNum = 0;
(这个东西和safe文件夹下的sysstat.properties好像啊,看看先:
#sysstat.properties
#Mon Jul 16 11:35:16 CST 2007 //appearTimeStr ??
onlineNum=2
postMainNum=1
allUserNum=2
appearTime=963209825828
postNum=1
lastRegUser=sgwood
)下面有其get/set方法,子类可以继承哦!当然,还有一个子类需实现的方法:
public abstract void load();
public abstract void saveOnline(long nowonlinenum);//在线数
public abstract void saveAllUserNum(long allusernum, String lastreguser);//所有用户数和最后注册用户名
public abstract void savePostNum(long main, long all); //存入主题贴和总数
接着我们看下SysStatServiceImp.java文件好了:它加入了logger和UserConfig对象先,看下面的load方法:
public void load() {
Properties prop = new Properties();
File f = new File(this.getUserConfig().getSafePath() + "sysstat.properties"); //getSafePath()当然会暴露出来!
if (f.exists()) {
try {
FileInputStream fis = new FileInputStream(f);
prop.load(fis);
/**只需传递这个文件的 InputStream 给 load() 方法,就会将每一个键-值对添加到 Properties 实例中。然后用 list() 列出所有属性或者用 getProperty() 获取单独的属性。
list() 方法的输出中键-值对的顺序与它们在输入文件中的顺序不一样。 Properties 类在一个散列表(hashtable,事实上是一个 Hashtable 子类)中储存一组键-值对,所以不能保证顺序。
*/
this.setOnlineNum(Long.parseLong(prop.getProperty("onlineNum", "0").trim()));//继承过来的!!!
this.setAppearTime(Long.parseLong(prop.getProperty("appearTime", "0").trim()));
this.setAllUserNum(Long.parseLong(prop.getProperty("allUserNum", "0").trim()));
this.setLastRegUser(prop.getProperty("lastRegUser", ""));
this.setPostMainNum(Long.parseLong(prop.getProperty("postMainNum", "0").trim()));
this.setPostNum(Long.parseLong(prop.getProperty("postNum", "0").trim()));
this.setAppearTimeStr(Util.formatDateTime(new Date(this.getAppearTime())));//写时间用
fis.close();
} catch (NumberFormatException ex) {
logger.error(ex);
} catch (FileNotFoundException ex) {
logger.error(ex);
} catch (IOException ex) {
logger.error(ex);
}
} else {
save(); //文件不存在时!内部private方法哦
}
}
private void save() {
String path = this.getUserConfig().getSafePath() + "sysstat.properties";
Properties prop = new Properties();
prop.setProperty("onlineNum", String.valueOf(this.getOnlineNum()));
prop.setProperty("appearTime", String.valueOf(this.getAppearTime()));
prop.setProperty("allUserNum", String.valueOf(this.getAllUserNum()));
prop.setProperty("lastRegUser", this.getLastRegUser());
prop.setProperty("postNum", String.valueOf(this.getPostNum()));
prop.setProperty("postMainNum", String.valueOf(this.getPostMainNum()));
try {
FileOutputStream fos = new FileOutputStream(path);//写入新文件中
prop.store(fos, "sysstat.properties");
fos.close();
} catch (FileNotFoundException ex) {
logger.error(ex);
} catch (IOException ex) {
logger.error(ex);
}
}
我们看下对抽象类的实现吧:(先load一下资源文件,再set相关的key值,最后用私有的save一下)
public void saveAllUserNum(long allusernum, String lastreguser) {
this.load();
this.setAllUserNum(allusernum);
this.setLastRegUser(lastreguser);
this.save();
}
public void saveOnline(long nowonlinenum) {
this.load();
if (nowonlinenum > this.getOnlineNum()) { //好象不太对,不然只有多没有少!
long atime = System.currentTimeMillis();
this.setOnlineNum(nowonlinenum);
this.setAppearTime(atime);
this.setAppearTimeStr(Util.formatDateTime(new Date(atime)));
this.save();
}
}
public void savePostNum(long main, long all) {
this.load();
this.setPostMainNum(main);
this.setPostNum(all);
this.save();
}
OK!终于可以回到BoardServiceCachImp实现类中了,绕了好大一个弯!由于还有许多东西没用过,只能推测一下其用法了哦~~~还是先看几个,createBoard(Board board)先:
@SuppressWarnings("unchecked")
public Board createBoard(Board board) throws BbscsException {
try {
Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父级版区
if (pboard != null) { // 父级版区存在
List pboards = new ArrayList();
pboards.addAll(pboard.getParentIDs());//父的祖先
pboards.add(pboard.getId());//父
/** addAll(Collection c)
add(int index,Elelemt e)
注意parentIDs和childIDs均为List是由自定义userType的
public Class returnedClass() {
return List.class;
}
决定返回是List类型的!
*/
board.setParentIDs(pboards); // 设置父级版区列表字段
board.setLevel(pboard.getLevel() + 1); // 设置级别,在父级别上+1
}
board = this.getBoardDAO().saveBoard(board);//这里的其它参数由添加时决定,不需要改变,或由其它因素相关!由DAO完成实质的工作
if (pboard != null) {
List pcboards = this.getBoardDAO().findBoardsByParentID(board.getParentID(), 1, -1,
Constant.FIND_BOARDS_BY_ORDER);
/** 取得父级半区的所有子版区列表pcboard,1是useStat,-1是hidden,FIND_BOARD_BY_ORDER=0另外有,
public static final int FIND_BOARDS_BY_MAINPOSTNUM = 1;
public static final int FIND_BOARDS_BY_POSTNUM = 2;
/*
List cids = this.getBoardIDs(pcboards);//得到子版ID的List
/**
public List getBoardIDs(List boards) {
List<long></long> l = new ArrayList<long></long>();
for (int i = 0; i < boards.size(); i++) {
Board b = (Board) boards.get(i);
l.add(b.getId());
}
return l;
}
*/
pboard.setChildIDs(cids); // 设置父级版区的所有子版区列表字段
this.getBoardDAO().saveBoard(pboard);//更新父信息
this.getBoardCache().remove(pboard.getId());
/**从Board Cache中清除, 我们看下BoardCache的bean定义:
<bean id="boardCache"></bean> class="com.laoer.bbscs.service.imp.OsCacheImp"> 仍然是这个,以前讲过!
<constructor-arg></constructor-arg>
<value></value>${cache.config} cache.config=oscache.properties
public void remove(Object key) {
logger.debug("Remove from cache [Key:" + key + "]");
this.admin.flushEntry(key.toString());
}
从oscache.properties配置处的Cache内容中去掉key=pboard.getID()的对象,让OSCache自动缓存内容吧!
*/
}
this.clearBoradListSysListCache(board.getParentID());
/**它其实是一私有方法哦!需要注意的是实质用了SysListOjbCache,它其中的对象标识竟是[][][][][],5555,可能有些对象后三者可不用:
private void clearBoradListSysListCache(long pid) {
String[] useStats = { "-1", "0", "1" };
String[] hiddens = { "-1", "0", "1" };
String[] orderTypes = { "0", "1", "2" };
for (int i = 0; i < useStats.length; i++) {
for (int j = 0; j < hiddens.length; j++) {
for (int x = 0; x < orderTypes.length; x++) {
this.getSysListObjCache().remove(
"[B][" + pid + "][" + useStats[i] + "][" + hiddens[j] + "][" + orderTypes[x] + "]");
}
}
}
}
*/
// 为版区增加用户组版区权限
List gl = this.getUserGroupDAO().findUserGroupsAll();
/**取得用户组列表
public List findUserGroupsAll() {
return this.getHibernateTemplate().find(LOADS_ALL);
}
private static final String LOADS_ALL = "from UserGroup order by id";
原始数据为6个记录的表(见数据库),id1---6,以GroupName区分之,TypeID表示代表类型,默认是系统类型的,不能删除,用户自己建的就是另外一个类型了。
*/
BoardPermission bp;
for (int i = 0; i < gl.size(); i++) {
UserGroup ug = (UserGroup) gl.get(i);
bp = new BoardPermission();
bp.setBoardID(board.getId().longValue());//冗余字段
bp.setGroupID(ug.getId().intValue());//冗余字段
switch (ug.getId().intValue()) {
case 1:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1);
/**
<property name="permissions" type="com.laoer.bbscs.ext.hibernate.SplitList" column="Permissions"></property>
break;
case 2:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_2);
break;
case 3:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_3);
break;
case 4:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_4);
break;
case 5:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_5);
break;
case 6:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_6);
break;
default:
bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1);
}
this.getBoardPermissionDAO().saveBoardPermission(bp);
/**而在Constant.java中有段static段:
for (int i = 0; i < BOARD_PERMISSION_GROUP_1.length; i++) {
BOARD_PERMISSION_GROUP_LIST_1.add(new Long(BOARD_PERMISSION_GROUP_1[i]));
}
for (int i = 0; i < BOARD_PERMISSION_GROUP_2.length; i++) {
BOARD_PERMISSION_GROUP_LIST_2.add(new Long(BOARD_PERMISSION_GROUP_2[i]));
}
for (int i = 0; i < BOARD_PERMISSION_GROUP_3.length; i++) {
BOARD_PERMISSION_GROUP_LIST_3.add(new Long(BOARD_PERMISSION_GROUP_3[i]));
}
for (int i = 0; i < BOARD_PERMISSION_GROUP_4.length; i++) {
BOARD_PERMISSION_GROUP_LIST_4.add(new Long(BOARD_PERMISSION_GROUP_4[i]));
}
for (int i = 0; i < BOARD_PERMISSION_GROUP_5.length; i++) {
BOARD_PERMISSION_GROUP_LIST_5.add(new Long(BOARD_PERMISSION_GROUP_5[i]));
}
*/
}
return board;
} catch (Exception e) {
logger.error(e);
throw new BbscsException(e);
}
}
从创建中可以知道,它调用了两个cache(sysListObjCache和boardCache,对父级和本级的相关项进行更新(或产生),再保存.还有就是对此分版区(论坛)的用户权限的写入.总的来说,原则上,要数据服务(保存和查找)从DAO层来,要Cache从服务层来,要常用服务也从服务层来(因为它不参与DAO工作)
接下来,我们继续看findBoardAllTree:
@SuppressWarnings("unchecked")//类型安全问题
public List findBoardsAllTree(long pid, List topList, int useStat, int hidden, int orderType) {
List l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden, orderType);//这个pid指的是当前论坛,与前面的pid不一样哦
for (int i = 0; i < l.size(); i++) {
Board b = (Board) l.get(i);
topList.add(b);
this.findBoardsAllTree(b.getId().longValue(), topList, useStat, hidden, orderType);//由于topList是一个引用类型,可递归得到All子论坛及子论坛的子论坛...
}
return topList;
}
而这个服务类的findBoardByParentID(非DAO)它首先是查询SysListObjCache里面有没有,若没有的话再由DAO从数据库中找,且放入到SysListObjCache中,用了add方法,需注意的是其命名有点怪哦!
public List findBoardsByParentID(long pid, int useStat, int hidden, int orderType) {
List l = (List) this.getSysListObjCache().get(
"[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]");
if (l == null) {
// l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden,
// orderType);
l = this.getBoardDAO().findBoardIdsByParentID(pid, useStat, hidden, orderType);
this.getSysListObjCache().add("[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]", l);
}
List<board></board> bl = new ArrayList<board></board>();
if (l != null && !l.isEmpty()) {
for (int i = 0; i < l.size(); i++) {
// Board b = this.getBoardByID(((Long)l.get(i)).longValue());
Board b = this.getBoardByID((Long) l.get(i));
if (b != null) {
bl.add(b);
}
}
}
由于Cache中不一定是Borad对象了,由于add进的是list,便由List<board></board>类型的bl重新加载一次为Board的List.findBoardInIDs根据IDs一个取一个,边放入到l中!
public List findBoardsInIDs(List ids) {
List<board></board> l = new ArrayList<board></board>();
if (ids != null && !ids.isEmpty()) {
for (int i = 0; i < ids.size(); i++) {
// Board b = this.getBoardByID(((Long) ids.get(i)).longValue());
Board b = this.getBoardByID((Long) ids.get(i));
if (b != null) {
l.add(b);
}
}
}
return l;
}
return bl;
}
与其相反的方法是:
public List getBoardIDs(List boards) {
List<long></long> l = new ArrayList<long></long>();
for (int i = 0; i < boards.size(); i++) {
Board b = (Board) boards.get(i);
l.add(b.getId());
}
return l;
}
注意以上都有this.getBoardByID(),它是带Cache的哦!从BoardCache中取!
public Board getBoardByID(long id) {
Board board = (Board) this.getBoardCache().get(new Long(id));
if (board == null) {
board = this.getBoardDAO().getBoardByID(id);
if (board != null) {
this.getBoardCache().add(board.getId(), board);
}
}
return board;
}
另外,对于Permission,有以下几个方法:
public Map[] getBoardMasterPermission(int roleID)
public Map[] getBoardPermission(long bid, int groupID)
private Map[] getPermissionMaps(long bid, int groupID)
private Map[] getPermissionMaps(int roleID)
前面2个是公开的方法,后2个是被调用的..
public Map[] getBoardMasterPermission(int roleID) {
if (Constant.USE_PERMISSION_CACHE) {
Map[] mapPermission = (Map[]) this.getUserPermissionCache().get("R_" + String.valueOf(roleID));
if (mapPermission == null) {
mapPermission = this.getPermissionMaps(roleID);
this.getUserPermissionCache().add("R_" + String.valueOf(roleID), mapPermission);
}
return mapPermission;
} else {
return this.getPermissionMaps(roleID);
}
}
private Map[] getPermissionMaps(int roleID) {
Map[] mapPermission = { new HashMap(), new HashMap() }; //MAP数组,555
Role role = this.getRoleDAO().findRoleByID(roleID);//得到id为roleID的角色对象
List permissions = role.getPermissions(); // 取得角色的权限ID列表(ID的List)
if (permissions != null && !permissions.isEmpty()) {
List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得权限列表Permission对象的List
for (int i = 0; i < permissionList.size(); i++) {
Permission permission = (Permission) permissionList.get(i);
if (permission.getTypeID() == 2) {
mapPermission[0].put(permission.getResource() + "," + permission.getAction(), permission);
}
if (permission.getTypeID() == 3) {
mapPermission[1].put(permission.getId(), permission);
} //这段需理解!!!
}
}
return mapPermission;
}
另外,除了版主外,还有版区权限:
public Map[] getBoardPermission(long bid, int groupID) {
if (Constant.USE_PERMISSION_CACHE) {
Map[] mapPermission = (Map[]) this.getUserPermissionCache().get(
"BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID));
if (mapPermission == null) {
mapPermission = this.getPermissionMaps(bid, groupID);
this.getUserPermissionCache().add("BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID),
mapPermission);
}
return mapPermission;
} else {
return this.getPermissionMaps(bid, groupID);
}
}
同样,它用了类似的重载方法:
private Map[] getPermissionMaps(long bid, int groupID) {
Map[] boardPermission = { new HashMap(), new HashMap() };
BoardPermission bp = this.getBoardPermissionDAO().findBoardPermissionByBidGid(bid, groupID);//用的是BoardPermissionDAO
List permissions = bp.getPermissions(); // 取得权限ID列表
if (permissions != null && !permissions.isEmpty()) {
List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得权限列表Permission对象的List
for (int i = 0; i < permissionList.size(); i++) {
Permission permission = (Permission) permissionList.get(i);
if (permission.getTypeID() == 2) {
boardPermission[0].put(permission.getResource() + "," + permission.getAction(), permission);
}
if (permission.getTypeID() == 3) {
boardPermission[1].put(permission.getId(), permission);
}
}
}
return boardPermission;
}
接下来,看看getNextOrder(long pid),getPostSumNum(int mainorall,int usStat,int hidden),它们完全由DAO去查询数据库!我们看下remove系列:
public void removeBoard(Board board) throws BbscsException {
try {
Long lbid = board.getId();
long pbid = board.getParentID();
Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父版区
this.getBoardDAO().removeBoard(board); // 删除版区
this.getBoardPermissionDAO().removeBoardPermissionsByBid(board.getId().longValue());//删除对应版区的权限
if (pboard != null) { // 父版区存在,对ChildIDs字段做矫正
List pcboards = this.getBoardDAO().findBoardsByParentID(pboard.getId().longValue(), 1, 0,
Constant.FIND_BOARDS_BY_ORDER);//pcboards是子对象
List cids = this.getBoardIDs(pcboards);//cids是子IDs
pboard.setChildIDs(cids);
this.getBoardDAO().saveBoard(pboard);
}
this.getBoardCache().remove(lbid);
this.clearBoradListSysListCache(pbid);
//清理本id的BoardCache,清理父id的SysListObjectCache,而userPermission中出现过去2类:R_ BG_的..都不好清理!
} catch (Exception ex) {
logger.error(ex);
throw new BbscsException(ex);
}
}
public void removeBoardTag(Board board, String tagID) throws BbscsException {
BoardTag bt = null;
Iterator it = board.getBoardTag().iterator(); //由Iterator遍历查找,找到后remove掉
while (it.hasNext()) {
bt = (BoardTag) it.next();
if (bt.getId().equals(tagID)) {
board.getBoardTag().remove(bt);
break;
}
}
try {
board = this.getBoardDAO().saveBoard(board); //保存修改结果
this.getForumDAO().updateForumsTag(tagID, "0", "");//帖子TAG
this.getForumHistoryDAO().updateForumsTag(tagID, "0", "");//历史贴TAG
this.getBoardCache().remove(board.getId());//BoardCache清理一次
} catch (Exception e) {
logger.error(e);
throw new BbscsException(e);
}
}
最后再看两个方法:
public Board updateBoard(Board board, long oldParentID) throws BbscsException {
try {
Board pboard =
- bbscs8详细分析.rar (119.5 KB)
- 描述: 所有四篇文档
- 下载次数: 668
评论
QQ10046417
MSN:kson34@hotmail.com
发表评论
-
C3P0连接池详细配置
2008-03-28 10:06 1214转载至:http://jacreater.spac ... -
Eclipse中设置在创建新类时自动生成注释(转)
2008-03-28 10:03 1619Eclipse中设置在创建新类时自动生成注释 windo ... -
struts2 code-behind
2008-03-01 09:39 4690code-behind在struts2里有两种表现形式: 1. ... -
(转)Apache2.2和SVN1.4.4搭建svn版本控制平台windows版
2007-12-23 14:04 2995一、安装 第一步,安装Apache2.2,下载后直接安装就可 ... -
Resin 3.x 经验总结
2007-12-09 20:51 15961. 怎样关闭目录浏览 ... -
缓存管理框架原理(JCS)
2007-12-05 12:07 4044安装和配置 在web应 ... -
截取在线编辑器的字符串怎么处理
2007-11-22 10:39 1550/** * 按字节长度截取字符串(支持截取带HTML代码 ... -
Java对各种文件的操作详解(转)
2007-11-22 10:37 1894java中提供了io类库,可以轻松的用java实现对文件的各种 ... -
java时间操作函数汇总(转)
2007-11-22 10:27 16131.计算某一月份的最大天数 Calendar time=Cal ... -
很好面试题(JSP程序员) 转
2007-11-13 14:34 1887答题时间20分钟 1. System.out.printl ... -
Resin的基本使用
2007-09-15 19:44 6460使用Resin开发Java Web项目时,需要建立自己的W ... -
怎样才能成为更有创意博客
2007-05-18 06:14 1041上周在清理我的硬盘的时候,发现我的一篇老文章,说的是我给一些年 ... -
模型驱动SOA帮助提高开发团队效率
2007-07-23 06:26 974做过应用软件开发的朋友们大多都熟悉传统的开发生命周期:应用软件 ... -
WebWork教程三
2007-08-01 11:20 1489验证框架 WebWork提 ... -
WebWork教程二
2007-08-01 11:27 1825Action的单元测试 ... -
WebWork教程一
2007-08-01 11:31 2992WebWork介绍 WebWork是由OpenSymph ... -
对天乙社区bbscs8实现的详细分析三
2007-08-02 08:27 5021经过前面的分析,我们已经理清楚了业务层,接下来的部分将是web ... -
对天乙社区bbscs8实现的详细分析四
2007-08-07 09:07 3061在分析三,我们已经分析出jsp页面如何通过struts2的标签 ... -
对天乙社区bbscs8实现的详细分析二
2007-08-07 09:14 1658我们仍然将分析处于service包中,首分析下上次没有分析的F ...
相关推荐
【标题】:“[论坛社区]天乙社区修改版_bbscs7.rar”指的是一个针对名为“天乙社区”的在线论坛平台进行定制或优化后的版本,该版本被存档为RAR格式的压缩文件,名为“bbscs7”。 【描述】:“[论坛社区]天乙社区...
天乙社区论坛可能是一个开源的论坛软件,开发者或团队对其原始代码进行了修改和优化,以满足特定需求或提供额外的功能。二次开发通常包括界面改进、性能优化、增加新特性以及修复已知问题等方面的工作。 文件...
人脸识别项目实战
内容概要:本文详细描述了一个完整的Web应用程序的开发过程。该项目主要采用了Hono作为服务器框架,Prisma作为ORM工具,JWT用于认证鉴权,以及一系列现代化的最佳实践确保系统的健壮性和安全性。项目初期构建了基础架构,并设置了必要的依赖和工具。在后端方面涵盖了公共API接口的设计、CRUD增删改查逻辑、用户认证和授权等功能。此外还特别关注到了API的安全保护,如输入输出的校验,跨站请求伪造CSRF的防范,XSS防御等措施;为确保代码的质量引入了代码检测(比如ESLint搭配Prettier),并建立了完善的测试框架以保障后续开发阶段的功能正确。对于可能出现的问题预先定义了一组规范化的异常响应,并提供OpenAPI文档以方便开发者理解和调用。数据存储层面上利用了关系型与非关系型数据库各自的特性,实现了数据的有效组织,最后提供了实用的脚本,可用于种子数据插入以及执行必要的初始化工作。 适合人群:面向具有一定JavaScript/TypeScript开发经验,尤其是Node.js后台服务搭建经验的中级程序员和技术团队。 使用场景及目标:这份材料非常适合那些需要快速建立安全高效的RES
【资源介绍】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,也可以作为小白实战演练和初期项目立项演示的重要参考借鉴资料。 3、本资源作为“学习资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研和多多调试实践。 掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip
手势识别项目实战
(参考GUI)MATLAB BP的交通标志系统.zip
人脸识别项目实战
内容概要:本文详细介绍了 C++ 函数的基础概念及其实战技巧。内容涵盖了函数的基本结构(定义、声明、调用)、多种参数传递方式(值传递、引用传递、指针传递),各类函数类型(无参无返、有参无返、无参有返、有参有返),以及高级特性(函数重载、函数模板、递归函数)。此外,通过实际案例展示了函数的应用,如统计数组元素频次和实现冒泡排序算法。最后,总结了C++函数的重要性及未来的拓展方向。 适合人群:有一定编程基础的程序员,特别是想要深入了解C++编程特性的开发人员。 使用场景及目标:① 学习C++中函数的定义与调用,掌握参数传递方式;② 掌握不同类型的C++函数及其应用场景;③ 深入理解函数重载、函数模板和递归函数的高级特性;④ 提升实际编程能力,通过实例强化所学知识。 其他说明:文章以循序渐进的方式讲解C++函数的相关知识点,并提供了实际编码练习帮助理解。阅读过程中应当边思考边实践,动手实验有助于更好地吸收知识点。
Comsol光学仿真模型:包括纳米球 柱 Mie散射多级分解 ,Comsol光学仿真模型; 纳米球; 柱; Mie散射; 多级分解,Comsol光学仿真模型:纳米结构Mie散射多级分解
永磁同步电机全速域控制高频方波注入法、滑模观测器法SMO、加权切矢量控制Simulink仿真模型 低速域采用高频方波注入法HF,高速域采用滑膜观测器法SMO,期间采用加权形式切 送前方法 1、零低速域,来用无数字滤波器高频方波注入法, 2.中高速域采用改进的SMO滑模观测器,来用的是sigmoid函数,PLL锁相环 3、转速过渡区域采用加权切法 该仿真各个部分清晰分明,仿真波形效果良好内附详细控制方法资料lunwen 带有参考文献和说明文档,仿真模型 ,核心关键词: 1. 永磁同步电机; 2. 全速域控制; 3. 高频方波注入法; 4. 滑模观测器法SMO; 5. 加权切换矢量控制; 6. Simulink仿真模型; 7. 零低速域控制; 8. 中高速域控制; 9. 转速过渡区域控制; 10. 仿真波形效果; 11. 详细控制方法资料; 12. 参考文献和说明文档。,永磁同步电机多域控制策略的仿真研究
基于蜣螂优化算法的无人机三维路径规划【23年新算法应用】可直接运行 Matlab语言 主要内容:读取地形数据,利用蜣螂算法DBO优化三维路径,目标函数为总路径最短,同时不能撞到障碍物,效果如图所示,包括迭代曲线图、三维路径图、二维平面图等等 ,基于蜣螂优化算法;无人机;三维路径规划;总路径最短;障碍物避免;Matlab语言;迭代曲线图;三维路径图;二维平面图,蜣螂算法优化无人机三维路径规划:实时避障、路径最短新应用
清华大学2024年研究生复试上机考试题.zip
南京理工大学研究生入学考试2011年复试上机试题
手势识别项目实战
这里是3501的内容,用于复习资料
异步电动机变压变频调速系统,包含六千多字的文档、框架图、Simulink仿真模型,电力拖动、电机控制仿真设计 仿真模型+报告 开关闭环对比仿真都有,资料如图所见如所得 ,异步电动机;变压变频调速系统;六千字文档;框架图;Simulink仿真模型;电力拖动;电机控制仿真设计;开闭环对比仿真;资料如图。,异步电机控制仿真系统:六千字详解与图解
人脸识别项目实战
手势识别项目实战