花了将近两个月的时间学习了一个企业级进销存项目,已经结束了两周多,现在终于有时间来对这个项目的学习做个总结了!
一、首先介绍下这个项目
(注:本人目前大三,专业为信息管理,与编程沾边不多。而我对编程很感兴趣,从大一下期开始主要通过看书和视频自学学习java编程!)。
项目名称:手机进销存系统(Mobile SCM)
主要开发技术:Struts2+Spring+ibatis+jQuery
数据库:MySql5.1(Navicat客户端管理工具)
服务器:tomcat7.0
建模工具:ER/Studio
运行系统:Linux
其他技术:报表应用iReport+jasperReport技术,应用jxl进行excel导出。
项目介绍(视频原话):本系统由某手机贸易企业定制开发,合同价格9.8万元,开发周期3个月,5人开发完成。本系统既具有一般进销存系统功能,又有客户独有的需求,通过学习本课程,可以全面了解进销存类项目。
视频评价:
①.这是我在网上找的一套视频教程,总共80集,每集60分钟左右(需要很大的耐心啊!)。项目有点老,大概是12年左右录制的,但是学到的东西很多。
②.这是我看的视频中总时间最长的一套视频了,虽然有80集之多,但仍有很多模块功能的实现没有在视频中展现出来。但是,这套视频已经把该讲的都讲了,而且讲的很全面,学到了很多东西。
③.这套视频是基于现有的、企业实际在使用的系统来讲解,从售前的与客户沟通->开发阶段->售后维护阶段,让我了解到了公司项目开发流程。不过这套视频的着重点是在开发阶段,主要讲解了系统设计、建模工具的使用、主要模块功能实现等;像与客户沟通、具体参与项目需求分析、团队分工合作等还是得工作后具体参与其中才能真正有所体会。
④.视频讲解的内容也非常详细,就一个功能而言,详细地讲解了JS,页面构成,代码编写,文件配置等等,所以也能学到很多细节性的东西,某些内容会在后面写出来。作为一个开发人员,最主要的是需要了解项目的需求,根据需求来完成代码的编写。
实际学习:
①.首先,视频中的持久层框架使用的是iBatis,我因为在学习这套视频之前不久刚学习过了MyBatis框架,所以将iBatis换成了MyBatis,ibatis、mybatis和hibernate后面会具体说一说。总的来说,框架的使用感觉是非常简单的,在此之前就已经学习过了S2SH(Struts2+Spring+Hibernate),SSM(SpringMVC+Spring+MyBatis),学框架就是学配置!。
②.在实际学习时,视频中讲解系统设计等内容时是跟着视频认真听的,涉及到代码的编写等内容大部分是先了解模块功能的需求后,先自行编写代码实现功能后再快进地看视频,代码编写相对简单,主要还是业务需求。代码写多了之后会发现(刚刚统计了下代码量<仅统计了.java和.jsp>,到现在为止,已超过11W行啦(∩_∩)),无非就是增删改查,要说难一点的就是将需求转换为逻辑实现。
③.因为是练习,加上学校时间也比较紧,实际编写代码时,部分类似于表单验证的就没有具体做了;到后面部分模块也没有具体去实现了,因为基本都是些重复的工作了,在以前的学习和项目中代码写得够多的啦~\(≧▽≦)/~,而且视频总的来说也只实现了一半不到的功能,因为视频讲的非常详细,所以想要80集讲完所有功能不可能,也没有必要!!
④.因为讲得很详细,所以学到了以前很多没注意到的细节;在学习这套视频的同时,一方面学到了很多新知识,类似于建模工具ER/Studio的使用;另一方面也复习了以前的知识,并更加熟练地加以运用。在这期间,有些新的知识点或者解决的问题写了几篇新的博客(可以看之前的博客),有些则会在接下来的篇幅中进行总结!
二、项目相关截图展示
1.登录页面
2.首页
3.功能展示
菜单管理
权限管理
供应商业务
4.虚拟机运行效果(Linux)
5.工程目录结构
6.项目相关文件
三、项目学习总结
1.进销存系统概述
进销存系统也称作供应链管理系统(Supply Chain Management, SCM)。SCM基本内容:计划(决策管理层,主要体现在报表上)、采购、[制造(跟生产有关的企业)]、库存、销售、退换货管理。SCM总体为两大流程:物品流、财务流。
百度百科解释:进销存系统是对企业生产经营中进货、出货、批发销售、付款等进行全程跟踪管理,从接获订单合同开始,进入物料采购、入库、领用到产品完工入库、交货、回收货款、支付原材料款等,每一步都为您提供详尽准确的数据。有效辅助企业解决业务管 理、分销管理、存 货管理、营销计划的执行和监控、统计信息的收集等方面的业务问题。
系统总流程图:
2.项目组成员及分工(企业开发):我想刚进公司的新人则处于开发工程师这个阶段,然后一步步往上走!
项目经理(PM):与客户沟通、进度、成本、质量的控制、资源(人力资源)协调等。
系统分析师(SA)[架构设计师/技术经理]:技术带头人,需求分析、架构设计、指导开发、重要功能开发等。
开发工程师(SE):具体模块的设计与实现。
测试工程师(Test Engineer):测试用例编写、执行测试提交BUG列表。
3.项目开发流程
需求分析 -> 系统设计 -> 开发 -> 测试 -> 实施及试运行 -> 客户培训 -> 维护
项目开发流程图:
4.系统设计主要工作内容:
①分析需求、了解环境及限制等
②总体设计、完成子系统划分,并确定子系统间关联及通信方式
③对每个子系统进行模块及子模块划分,并确定之间关联
④每个模块输入、处理流程、输出
⑤设计数据结构
⑥设计用户界面
⑦完成《概要设计说明书》
5.概要设计说明书(主要内容参考《概要设计说明书主要的内容.doc》)
概要设计的目的就是希望一个从来没有接触过的人一看就能从各个方面都对系统的作用,功能,实现方面有一个大概的了解,并为以后的各类详细设计文档提供一个指引和方向。
6.《需求规格说明书》作用及主要内容(可具体参考《LSD-软件需求规格说明书.doc》)
作用:
①项目组成员、测试人员、公司审核部门的一个参照标准
②客户验收标准
主要内容:
①标题、编号、版本、作者、时间等信息
②变更记录
③项目概况、编写目的、目标读者、系统用户与环境等
④系统模块、总流程图等
⑤功能性需求
⑥性能性需求
7.模块设计
①分析模块流程
②根据需求确定输入、输出及处理逻辑
③设计用户界面
④面向对象的设计方法[UML(统一建模语言),时序图,状态图,类图等]
⑤以数据为中心的设计方法(数据建模)
8.MVC与分层设计(具体可网上查阅)
①MVC(Model-View-Controller):MVC是一种架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而达到更好的开发和维护效率。在MVC模式中,应用程序被划分成模型(Model)、视图(View)和控制器(Controller)三个部分。其中,模型部分包含了应用程序的业务逻辑和业务数据;视图部分封装了应用程序的输出形式(页面、界面);而控制器部分负责协调模型和视图,根据用户请求来选择要调用哪个模型来处理业务,以及最终由哪个视图为用户做出应答。MVC模式的这三个部分的职责非常明确,而且相互分离,每个部分都可以独立的改变而不影响其他部分,大大提高了应用的灵活性和重用性。
②为什么分层:便于重用、维护。
③三层结构及各层次的结构:业务逻辑层(Service):不要出现直接的数据访问;传进来一组数据,根据传进来的参数和业务逻辑的要求把业务逻辑实现,最后把要显示的数据传递出去。业务逻辑层是重用性最高的一层,是核心。
④这里想说一下Struts2的MVC:推荐一篇博客:Struts2与MVC基础入门
9.ER/Studio(专业的数据建模工具):
ERStudio:ERStudio是优秀的数据库建模软件,它不仅可以建立表、视图等模型,还可以建立多表间各种关系的模型,另外还可以根据模型生成表到数据库。
使用体会:在这次的学习中,使用频率较多的一个软件就是ERStudio了。在此之前每次做项目时,因为数据量都比较小,基本10张表左右,因此都没有使用过建模工具,一个初步的分析后就开始直接建表;之前一直会遇到的问题是在开发中对表结构的修改是比较麻烦的,就算只有10张表左右,比如修改了某张表名后,与其相关联的表都要去修改,比较麻烦;而且很难看出表与表之间的联系,容易遗漏东西。而在这种中大型项目中(在这个项目中只做了三个模块就已经有30张表了),如果没有提前进行一个好的表结构设计的话,对后面的维护将是灾难性的。所以如果有一个好的建模工具就可以解决这些问题了。
表关联设计:如果我们对数据库拥有完全的控制权限,创建数据库时,可以不用考虑表之间的关联,因为数据库维护关联关系会消耗一定的资源。我们可以通过程序来维护即可。
可以看看下图使用ERStudio建立的逻辑模型:我们可以通过图形界面很清晰的了解模块划分、表结构、表之间的关联(这里并没有很严谨的设计表之间的关联,主要通过程序来维护)等等。我们如果需要临时修改一个字段或是关联关系等,可以很容易的修改。而且最方便的是可以直接将该逻辑模型生成物理模型并导出sql文件,直接导入数据库就可以使用了。ERStudio连上数据库后,还可以把数据库中的表拉到可视化建模界面显示,表与表之间的联系能够清晰地展现出来。
10.编码的一些技巧
①可直接使用Spring的字符编码过滤器来设置UTF-8格式,虽然以前学过,但在实际使用时就忘了Spring还有这个功能,每次都是自己建立一个过滤器来设置字符编码。
1 <filter> 2 <filter-name>CharacterEncodingFilter</filter-name> 3 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 4 <init-param> 5 <param-name>encoding</param-name> 6 <param-value>UTF-8</param-value> 7 </init-param> 8 </filter> 9 <filter-mapping> 10 <filter-name>CharacterEncodingFilter</filter-name> 11 <url-pattern>/*</url-pattern> 12 </filter-mapping>
②写一个系统初始化类实现ServletContextListener来加载系统初始化需要的数据以及启动一个定时器来定时清除系统日志等功能。系统公用的东西(如系统标题)可写在配置文件里,系统初始化时就加载进来。
③系统用户表一般会设置一个用户状态,如启动,停用,删除等状态。一般来说,删除用户不会真正删除用户信息,而是设置其状态为删除状态。角色也是一样,会有一个状态信息,如果停用了就要禁止该角色下的权限。
④如果我们对数据库拥有完全的控制权限,创建数据库时,可以不用考虑表之间的关联,因为数据库维护关联关系会消耗一定的资源。我们可以通过程序来维护即可。
⑤权限系统独立出来成一个模块或项目,以方便以后可以重用。
⑥写一个BaseAction来放置一些常用的request,resposne等等,其它的action类如果要用到这些资源可以直接继承BaseAction。
⑦利用好struts的标签库。<%@ taglib uri="/struts-tags" prefix="s"%>
可以用其来按需求显示某些数据或者模块。比如:<s:if test=" ">...</s:if> <s:elseif test=" ">...</s:elseif>
以及利用好c标签来做页面的一些判断处理。比如:<c:choose><c:when test="${status.first}">...</c:when><c:otherwise>...</c:otherwise></c:choose>
1 <c:url value="${ctx}/mobileBasic_delete.action" var="delUrl"> 2 <c:param name="table" value="basic_config"></c:param> 3 <c:param name="id" value="${config.id}"></c:param> 4 </c:url> 5 <a href="${delUrl}" onclick="return confirm(‘确定要删除吗?‘);">删除</a> 6 //这种方式也可以对中文进行编码
⑧测试的时候记得经常清空浏览器缓存(尤其是js文件更改后),否则某些数据一直测试不正常。以及,测试的时候个人觉得最好还是用火狐浏览器,用好火狐的firebug能节省很多时间。
⑨以前一般点击某些功能简单的按钮时,会用js来实现。现在才意识到还可以这样:
例如使用input按钮来简单跳转页面:<input type="button" class="btn cancel" value="取消" onclick="location=‘menu_menuDetail.action?menu.id=${menu.id}‘"/>
⑩不要忘了可以自定义标签来处理某些问题,代替某些js代码以及<%= %>。与jsp页面整体统一。自定义标签一是可以自定义类继承SimpleTagSupport,在doTag方法里实现逻辑处理,此种方式需要在xml中配置tag。另一种方式是在静态类中写一个静态方法处理逻辑,此种方式需要在xml中配置function,前台的书写方式则是:${my:dateFormat(beginDate)}
可参考个人博客:JSP自定义标签/自定义标签打包
以及,自定义注解、自定义拦截器、自定义标签等配合使用能更方便的编程。
?字符串处理时,StringBuilder速度更快,但是线程不安全的。SringBuffer是线程安全的,速度相比较慢。
?在类似于easyui这类框架下,我们点击菜单按钮跳转的页面往往是跳转到一个iframe中,但如果有这么一个业务需求:修改密码/退出系统,需要返回到首页,但如果直接让struts的result跳转到首页时,首页则显示到iframe中了,我们的需求是整个窗口刷新。这里有三种方法:
⑴js获取该按钮,利用ajax来实现,在action中处理后,返回时利用window.top.location = “”; 即可。
⑵使用一个临时页面,result配置转到该页面后,立即跳转。
⑶还有一种则是,临时生成一个脚本代码跳转:这种方式就非常简单了,以前怎么就没想到呢,反正都是通过js来跳转。
out.print(“<script type=’text/javascript’>top.location=index.action</script>”);
out.close();
?注销与退出系统的区别:注销系统:清除用户信息,返回首页;退出系统:清除用户信息,关闭浏览器。
?利用mybatis-generator自动生成javabean,sqlMap,dao等,就像利用HIbernate反向生成一样。我们使用建模工具建好逻辑模型后,生成物理模型,导出sql文件,再导入数据库,然后利用这个插件就可以自动生成javabean,sqlMap,dao等,极其方便。具体怎么安装插件和使用,这里给出一个百度链接:mybatis generator eclipse插件的安装
?日期:我们选择日期如2016-05-03 这个代表的是5月3日0时,即5月2日一整天,所以逻辑上我们如果获取5月3日的数据时,还需要加上一天的时间。
?使用Navicat查询创建工具完成复杂SQL构建:
?项目完成后可以将工具类留存。
11.遇到的一些小问题
①mybatis联合查询(association)的配置问题及某些细节:查看个人博客:MyBatis关联查询 (association) 时遇到的某些问题/mybatis映射
②一次偶然的断网条件下,启动系统发现报了如下错误:
1 DEBUG - Loaded schema mappings: {http://mybatis.org/schema/mybatis-spring-1.2.xsd=org/mybatis/spring/config/mybatis-spring-1.2.xsd, 2 http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd, 3 http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd=org/springframework/web/servlet/config/spring-mvc-3.1.xsd, 4 ....................................
网上查了下,原因是断网后,applicationContext.xml配置文件中指定的xsd文件读取不到了——也就是说xsd每次都是从网上读取的?
当然这是不怎么可取的,具体解决办法参考这个博客:Spring如何加载XSD文件。我这里出错的原因是版本不一致导致的。
③一定要将action配置@Scope(value="prototype"),配置成原型模式的,否则就算你重定向也会有意向不到的问题,会将原来的数据带到新的页面去。
④MyBatis的<foreach ></foreach>标签:
当collection为List<Map<String,Object>>的时候,而且我们要使用map的key和value,如下使用item:
<foreach collection="mapList" item="item" separator=",">
(#{item.key},#{item.value})
</foreach>
当遍历的集合collection为Map<String, Object>时,要使用key和value,如下使用:
<foreach collection="map" index="key" item="value" separator=",">
(#{key},#{roleId},#{value})
</foreach>
⑤登出系统时,要清空session所有信息。request.getSession().invalidate();//注:此处必须使用原始的session,不能使用框架的Map<> session,那只是个映射.
⑥表单设置为disabled="disabled"时,将无法提交该表单数据,可以设置为readonly
<input type="text" readonly="readonly" name="supplier.id" value="${supplier.id}"/>
⑦Jstl标签可以用在js中,但只能在jsp文件的js中,外部js文件不可用。
1 <c:forEach items="${mobileTypeList}" var="mobType"> 2 if("${mobType.id}" == mobileTypeId){ 3 $("#refPrice").html("¥${mobType.buyPrice}"); 4 } 5 </c:forEach>
12.关于JSON使用的一些问题
①从服务端返回的json格式数据 键/值都必须用双引号(无论是json文件,还是json数据),不要用单引号,否则会出现一些意想不到的问题,像数据解析不正确等。
看官网的说明:
正确格式:JSON数据格式
②Spring的json插件:
以前一直都是使用JSONObject,JSONArray来得到json格式数据。这次发现还可以直接添加一个json插件,就可以很轻松的返回json数据了。
步奏:
⑴添加struts2-json-plugin-2.3.28.jar包
⑵修改配置文件(注意绿色的地方)
1 <package name="dev" namespace="/" extends="json-default"> 2 <action name="menu_*" class="menuAction" method="{1}"> 3 <result name="menuTree" type="json"></result> 4 </action> 5 </package>
⑶Action中将要转化成json的数据放在全局中(成员变量)。不必在方法内像JSONObject那样转成字符串再输出。
//使用JSONObject转换数据 JSONObject jsonObject = new JSONObject(); //将data转换为json格式数据 String result = jsonObject.fromObject(data).toString(); //返回 response.write(result);
⑷在前台获取的时候,注意需要从data里面取出来,因为此时的data中可能包含多个数据:
function(data){
var json= [data.key];
}
⑸插件原理:在返回到前台时,框架会自动将有get方法的成员变量转换成json格式数据返回(所以在action中不要将具体逻辑方法名带get,还有Action中的service成员也不要设置get方法,否则极容易出错。如果非要带get,可以在这个方法上加注解;@JSON(serialize=false),使其不要序列化,就不会转成json了)。
这种插件方式比使用JSONObject虽然更方便,但不容易控制,JSONObject可以控制具体输出的类容。
1 JSONObject jsonObject = new JSONObject(); 2 //json配置 3 JsonConfig config = new JsonConfig(); 4 //设置需要过滤掉的字段 5 config.setExcludes(new String[]{"subMenuList"}); 6 //得到json数据 7 String result = jsonObject.fromObject(menuTree, config).toString();
而且使用JsonConfig可以注册转换器,比如将Date对象转换成字符串。
注册转换器:config.registerDefaultValueProcessor(target, defaultValueProcessor);
13.switch…case…
变量的使用:
switch(此处数据类型):switch后面的括号里面只能放int(byte,short,char)类型和String类型的数据
case后的数据类型不能为Integer:
14.MyBatis、iBatis、Hibernate:具体区别百度
ibatis:半自动ORM框架,需手动写sql语句;开发效率相对于hibernate较低,但是更灵活,适应性更广泛(在企业开发中,我们可能不能完全去控制数据库结构 );
mybatis:MyBatis 最强大的特性之一就是它的动态语句功能。其前身就是ibatis。
Hibernate:由hibernate自动生成sql语句;hibernate则需要在符合一定规范(如数据库结构必须规范)下才能发挥它的最大效用。
15.mybatis缓存:默认情况下是没有开启缓存的,除了局部的 session 缓存(一级缓存),可以增强变现而且处理循环依赖也是必须的。要开启二级缓存,需要在你的 SQL 映射文件中添加一行:<cache/>
四、项目相关资料
项目打包文件:手机进销存系统
视频分享:北风网-手机进销存系统
注意所有视频及资料仅供学习使用!!!
然后,说一下此时此刻的感受,通过自学学习的东西虽然有点多,但也比较杂,不能形成一套系统性的知识链;而且,越是到后面就越不想再学下去,主要是现在所学的东西没能有地方使用,有点类似于一身武艺没有用武之地的感觉。以前一直碍于学校的课程和其它的琐事没有找工作/兼职,虽然有做过一些课程上的小项目,但都不能满足我。大三马上就结束了,所以还是尽快找个工作,也只有在工作中才能用我所学,也能学到更多新东西,学习才更有动力、更有激情!