OA的学习--第二天的内容--日志和BaseDao

来源:互联网 发布:c语言基本算法 编辑:程序博客网 时间:2024/05/18 03:15

    现在写的是第二天的学习内容的总结,第一天是搭了SSH框架,并且将各个文件都进行了分包处理,今天的内容主要是1.接着昨天的,讲一下日志和log4j.properties的配置;2.抽取了BaseDao层的公共代码,用于对数据库进行增删改查;3.最后就是开始写一个功能,岗位管理,分析他的功能,分析出有多少请求和jsp文件,然后写代码.

    鉴于第一天的总结太多了,有一部原因是我跟着视频,一步一步讲的,所以会把第一步怎么操作,第二步怎么操作都写下来,这样的好处就是以后我就可以按着这个步骤来做,坏处就是太多了.所以这次,我试试只是写最后结果.

日志

    Junit测试SpringTest的时候,会发现Console命令窗口中的初始化提示信息是红色的,这是因为没有配置日志.配置的日志可以是log4j,可以是JDK logging,也可以是自己写的自定义日志.但是,若有多个日志工具,用哪个呢?

    可以用slf4j来控制,只要将slf4j和日志的jar(这是一个jar)放到项目中,他就会知道用哪个日志.这些jar可以在slf4j的文件夹中找到,如下图,若用slf4j-log4j12-1.6.1.jar,则表示用log4j日志,若添加slf4j-nop-1.6.1.jar则表示不用日志.并且slf4j的版本需要和slf4j-log4j的版本一样.并且每类日志的jar只能添加一个,否则slf4j就不知道如何处理了.


    所以最后往项目中添加两个jar,log4jjar以及slf4jlog4jjar.然后提示就会变成黑色.


    然后在log4j.properties资源文件中,配置日志的提示级别.级别有5,从高到低,依次是fatal,error,warn,infodebug,若是配置为debug级别,则所有信息都会提示.若是fatal,则只有重大错误才会提示.其中这里配置的是,因为开发所以需要多多打印信息,这样好改Bug,所以给项目设置为debug,而除了项目以外的(如Spring等的),他们的一般信息都不需要打印出来,则只有error级别才显示.

### direct log messages to stdout ###log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n### set log levels - for more verbose logging change 'info' to 'debug' ####其他是error才报错log4j.rootLogger=error, stdout#cn.itcast.oa显示的级别是debug,但是其他的都是errorlog4j.logger.cn.itcast.oa = debug

DAO

    我们需要和数据库操作,这个就相当于我们的D,不过javaDao,这个层就是用来对数据库进行增删改查等各种与数据库的操作的.而且,由于每个实体都需要进行增删改查,所以在三层中每个实体都有一个Dao.而为了灵活,会加上接口,那么就有接口和实现类,所以每个实体都有Dao接口和Dao实现类2个类.

    并且由于实体的操作基本都有增删改查,代码也都类似,所以若是每个类都单着写,那么就会有很多类似的代码分布在各个类中.基于这个原因,所以提取一个BaseDao,采用泛型,反射,将最基本的增删改查封到里面.然后其他类就基础这个BaseDao,这样基本的增删改查就不用写了.代码化的表示就这样.


    其中,各个接口的代码就不一一列举了,从上图中,就很容易看出该如何写代码了.主要是BaseDaoImpl类中,运用的泛型和反射.

    像是这样,由于需要将数据写入到数据库中,所以需要session.session,需要sessionFactorygetCurrentSesson()来获取,所以注入容器中的sessionFactory,@Resource,表示注入资源.而要spring注入sessionFactory,BaseDaoImpl实现类应该放在容器中.不过由于我们不用BaseDaoImpl的实例化对象,而是用他的子类,所以在子类上用@Repository注入就可以了.

   然后增删改没有太大问题,对于查,由于需要知道传入T的真实类型,所以需要用反射,来获取真实的类型.做法就是在无参构造函数中,写上反射代码,获取真实的值.这样只要实例化子类对象,就能获取到子类对应实体的真实类型.若以RoleDaoImpl举例.继承BaseDaoImpl,传入TRole类型.然后this.getClass().getGenericSuperclass(),其中this就是RoleDaoImpl对象,然后获取结果pt的第一个参数的真实类型就可以了,因为只传入了一个T.若是传入Map类型,key对应第一个参数,value对应第二个参数.写测试类,测试方法中只写一句代码 RoleDaoroleDao = new RoleDaoImpl();测试效果,结果为"clazz---> classcn.itcast.oa.domain.Role".

    后面使用时,clazz.getSimpleName(),获取T的类名,得到Role这种值.

    对于查询的结果,如返回集合结果,没有也不要返回null,最好返回一个空集合.但是若是返回new ArrayList()这种,它自带10个空间的数组,比较浪费空间,所以返回Collections.EMPTY_LIST就可以了.

public class BaseDaoImpl<T> implements BaseDao<T> {/** * 注入的factory */@Resourceprivate SessionFactory sessionFactory;private Class<T> clazz = null; public BaseDaoImpl() {// 使用反射技术得到T的真实类型// 获取当前new的对象的泛型的父类类型,得到一个泛型化的类型对象ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();// 获取第一个参数的真实类型this.clazz = (Class<T>) pt.getActualTypeArguments()[0];System.out.println("class --> " + clazz);}/** * 获取当前可用的Session,这样子类也可以用getSession()来获取session来实现自己的特殊方法. *  * @return */protected Session getSession() {return sessionFactory.getCurrentSession();}public void save(T entity) {getSession().save(entity);}public void update(T entity) {getSession().update(entity);}public void delete(Long id) {Object obj = getById(id);if (obj != null) {getSession().delete(obj);}}public List<T> getByIds(Long[] ids) {if(ids == null || ids.length ==0 ) {return Collections.EMPTY_LIST;} else{return getSession().createQuery(//"FROM " + clazz.getSimpleName() + " WHERE id in (:ids)")//.setParameterList("ids", ids)//.list();}}public T getById(Long id) {if (id == null) {return null;} else {return (T) getSession().get(clazz, id);}}public List<T> findAll() {return getSession().createQuery(//"FROM " + clazz.getSimpleName())//.list();} }

    而上面提到的作为继承了BaseDaoImpl的子类,@Repository注解,表示放到容器中.里面不用写任何代码,就能实现增删改查了.

@Repositorypublic class RoleDaoImpl extends BaseDaoImpl<Role> implements RoleDao {}

    最后在总结个类关系图.


岗位管理

    现在Dao层已经建立好了,要做第一个功能,比较简单的岗位管理,分析业务需求,发现需要一个实体Role(岗位),以及基本方法增删改查.

    操作流程,可以看这个表.先设计实体,然后分析功能,有几个请求,然后依次写Action,Service,DaoJSP页面.


设计实体/建表

    所以首先第一步,建立实体,分析字段,只需要Long型的id,String类型的namedescription(岗位说明),然后生成getter,setter方法.写映射文件Role.hbm.xml,主键为native策略,自增.其他是一般属性,很好写的.

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >  <hibernate-mapping package="cn.itcast.oa.domain"> <class name="Role" table="itcast_role"> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property> <property name="description"></property>                </class> </hibernate-mapping>

    然后将该映射文件写到hibernate.cfg.xml,然后就要创建表了,而什么时候会创建表呢?在创建sessionFactory的时候就会去检测和执行创建表.所以执行springTest的测试sessionFactory的测试方法,就可以创建表,这个里面就创建了sessionFactory.

<!-- 导入映射文件 --><!-- <mapping resource="cn/itcast/oa/domain/User.hbm.xml" /> --><mapping resource="cn/itcast/oa/domain/Role.hbm.xml" />

Action

    然后实体和表都建好了,分析功能,对应的请求.

    分析,岗位管理,有一个岗位列表功能,可以查询所有的岗位;在列表页面上有添加,修改和删除的按钮,其中添加和修改都会弹到新页面,然后完成操作之后,点击提交,再重定向到列表页面.而删除是弹出提示框,询问是否确定删除,确定就删除.所以列表1个请求,添加/修改各2个请求,删除一个请求,所以共4个功能,6个请求,所以需要6Action方法,每个Action方法处理一种请求.并且还需要3个页面,list页面,添加页面和修改页面(之后会将添加/修改合并成一个页面)

    而其中添加/修改一定是重定向到列表页面,因为重定向是2个请求,会改变url的路径,而转发是一个请求,不会改变url路径,所以若添加完毕之后是转发到列表页面,那么此时url还是添加的url,刷新有可能就会将记录再添加一遍.


    然后写Action代码,先分析,对于6个请求,都起上名字,他们的返回值字符串,有页面的需要在struts.xml配置对应页面,没有页面的,像是删除/添加/修改之后回到列表页面的,不能直接返回listlist.jsp页面,这样数据没有经过Action准备数据,页面是空的.所以需要写成"toList",然后配置为重定向到role_list,经过Action执行list()方法,再到list.jsp页面,就有数据了.


    这幅图说明,所有的请求都应该经过Action来准备数据,再到JSP页面中去显示数据.


    代码是这样的,Action中先是没有写Service调用的,只是最基本的方法.每个方法都只写了一句return "";

@Controller@Scope("prototype")public class RoleAction extends ActionSupport {/** * 列表 *  * @return * @throws Exception */public String list() throws Exception {return "list";}/** * 删除 *  * @return * @throws Exception */public String delete() throws Exception {return "toList";}         //其他4个省略 }

    然后配置struts.xml中的roleaction.其中classroleAction,应该是从spring容器拿到的.所以需要将Action放入到容器中.所以所有的Action上都要写上注解@Controller表示放入容器中,@Scope("prototype"),表示该Action是多例的.

    role_*表示匹配所有的岗位管理的请求操作,然后method中的{1},表示获取到第一个*号对应的值.若请求为role_list,methodlist,所以会执行RoleActionlist()方法.返回list方法返回"list"字符串作为result,再次经过Action被接收到,然后list对应list.jsp页面,所以数据会显示到list.jsp页面上.

<!-- 岗位管理 --><action name="role_*" class="roleAction" method="{1}"><result name="list">/WEB-INF/jsp/roleAction/list.jsp</result><result name="addUI">/WEB-INF/jsp/roleAction/addUI.jsp</result><result name="editUI">/WEB-INF/jsp/roleAction/editUI.jsp</result><result name="toList" type="redirectAction">role_list</result></action>

    然后建立list页面和addUIeditUI共三个页面.每个页面只是写上"list","addUI","editUI",表明他们的名字,然后测试效果.启动tomcat,输入url,请求为role_list.action,会到list()方法上,最后显示list.jsp页面.同理addUIeditUI.


 

Service

    现在Action层的初步效果已经可以了,那么需要通过和数据库打交道,拿到数据真正的显示到界面上去.而一般采用的三层,ActionDao中间,还有一层Service.Action调用Service,Service调用Dao,Dao操作数据库,一层一层拿到数据.

    所以在Action中的代码需要修改,首先由于需要Service,所以要建立Service接口和实现类各一个.而采用的又是注入的方式,所以Service的实现类必须交由容器来管理,@Service来放到容器中,然后用servicefindAll()方法,ServicefindAll方法内部,是去调用了DaofindAll().最后将返回的集合,放到值栈的Map,可以回显到JSP页面上,使用OGNL#号就可以直接获取到.

    然后对于删除,前台会传递一个id过来,然后根据id来删除岗位,而如何接收id?目前是通过直接写一个Long型的id,然后给他getter,setter方法,struts2就可以自动封装参数.

@Resourceprotected RoleService roleService;//获取删除时,前台传递过来的id值 private Long id;  public Long getId() { return id; } public void setId(Long id) { this.id = id; }/** * 列表 *  * @return * @throws Exception */public String list() throws Exception {List<Role> roleList = roleService.findAll();// 拿到集合,显示到页面,放在值栈中,获取用OGNL的#号ActionContext.getContext().put("roleList", roleList);return "list";}/** * 删除 *  * @return * @throws Exception */public String delete() throws Exception {roleService.delete(id);return "toList";}

    必须交由容器管理的Service的实现类就是这样.Service接口类就不介绍了.

    并且除了需要交由容器管理,业务层还需要开事务,这样出了问题就会回滚了,所以还要写@Transactional,来开事务.然后里面是调用Dao层的各个方法,所以要注入roleDao,当然要注入roleDao,那么RoleDaoImpl一定也要交由容器管理,所以实现类上也写了@Repository.

    所以我现在初步总结的是.都是交由容器来管理,让它来实例化,但是Action是写@Controller,Service是写@Service,Dao是写@Repository,则普通的pojo类是写@Component.并且ServiceDao的这个注解都是写在实现类上.还有被注入的对象的实现类,一定要在容器中,否则如何注入.

@Service@Transactionalpublic class RoleServiceImpl  implements RoleService {@Resourceprivate RoleDao roleDao;public List<Role> findAll() {return roleDao.findAll();}public void delete(Long id) {roleDao.delete(id);}}

JSP页面

    这样到目前为止,ActionService,Dao都写完了,所以写JSP页面的内容,list.jsp,将放到Map中的roleList的数据遍历显示到界面上.使用struts的标签,然后用标签的遍历器,遍历roleList,然后取出roleList中的Role对象的属性id,namedescription,依次显示,删除则添加了role_delete的请求并带上参数id,id的值通过%{id}获得,这是OGNL的写法.

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>    <title>My JSP 'list.jsp' starting page</title>  </head>    <body>      <s:iterator value="#roleList">  <s:property value="id"/> ,  <s:property value="name"/>,  <s:property value="description"/>,  <s:a action="role_delete?id=%{id}" onclick="return confirm('确定要删除吗?')">删除</s:a>  <br/>  </s:iterator>  </body></head>

    手动往数据库中写几条数据,然后效果就是这样,点击删除就会删掉数据.


    以上就是第11集到18集的内容.内容不是很多,主要是说明了如何简单的加日志,同样是需要jar和配置文件.以及运用反射和泛型抽取BaseDao,达到代码复用.最后在这个的基础上,一步一步完成了岗位管理的findAll和删除功能.其中,抽取BaseDao,使用反射获取泛型的真实类型,感觉最重要.

    总结,比第一天的应该短了不少了,有进步,继续努力.

2 0