SSM之旅
经常在百度 google 做伸手党,知乎 github stackoverflow瞎逛,在很多大神的博客中学了很多,也在知乎上面看到,一个有好习惯的程序员是应该在工作一年开始写博客的,虽然不知道该写些什么和怎么写,但是我觉得慢慢开始养成这个习惯也是好的。
然后写一个controller
通过这个链接:http://localhost:8090/SpringMybatisDemo/test.do
就能看到这个信息了:
先搞搞student的信息吧,写一个student的controller,继承AbstractController
现在这个StudentMapper已经由spring代理了,但是我还是要获得spring的context才能获得StudentMapper的对象,于是写了个StudentService专门为StudentController服务:
StudentService的上层是DefaultService:
但是这样我的Studentcontroller就要new 一个StudentService的实例了,增加了耦合度,有没什么办法让我把StudentService注入到StudentController里面呢,于是就想到了把service注入到controller里。
既然想到就开始干吧,于是被一堆错误卡了很久......
后来重新换了个tomcat
在网上查了一下,很多推荐用SpringContextUtil implements ApplicationContextAware,这里我也用这种方法吧:
在applicationContext.xml中再加上这个bean:
加上service:
这样访问student.do的时候,就会跳到StudentController中,然后调用studentService的方法,从数据库中找到Student信息,返回到页面上:
student.jsp:
嗯,虽然是简陋了一点,但是至少是可以操作的了:
ok,现在可以继续做“编辑”操作了,用同一个controller。
不跳转,自然想到用ajax,这里我们没有用表单,所以就用超链接的方式,以GET的方法把参数放在 url里。
后台用@RequestParam获取 前端通过请求url 传过来的参数,并包装成JSONObject 回写到页面中:
前端获取到json数据并对json数据进行解析:
现在是可以达到大概的效果了,可是到前端的中文都变成了问号“?”,这应该是编码不一致导致的,后台调一下编码试试。
response.setCharacterEncoding("UTF-8");
在StudentController中加上这段代码,前端的中文就可以正常显示了。
但如果每个controller都要加,代码就重复了,能不能在更外层的地方,只设置一次,之后就不用再设置了呢?
spring这里也为开发者提供了方法:
在web.xml中加入以上配置,就能让程式默认呢使用utf-8编码。以上配置大部分都比较好理解,有点不好理解的地方是forceEncoding,
所以这里我们需要设置为true。
后台传到前台的数据是json格式,那前台传到后台的格式也用json好了:
service要增加一个方法了,这里发现service中的mapper每次都要初始化,同样造成了代码重用,所以这里也在web.xml中配置一下mapper的bean,然后把mapper注入到service里:
Studentservice的studentMapper属性也增加一个setter方法:
service和controller都加上update方法:
这个时候发现,update之后,事务并没有自动提交,现在暂时不想配置事务管理,所以把datasource的配置换成一个有自动commit的bean
加上defaultAutoCommit,嗯 大功告成:
CRUD,还差增删,那我们就搞一搞增加一个学生:
新增学生和更新学生的操作是一样的,用的是同一个form。
删除一个学生:
增加&删除学生:
到这里,单表的增删改查就完成了,接下来要进行两张表的操作。
这里被MBG 生成的一个Key类搞了一段时间,我发现其中一个关联table的po类并没有key类,后来又去生成一次,仔细观察MBG日志才发现,在table里面的不同用户下还有一个同名的table,原来如此,drop掉其他用户的table,继续。
发现界面有点不好看,加了些bootstrap的样式:
先把Score的增删改查做一下:
由于Score是load出来的,为了增加用户体验,在编辑完成之后希望直接看到更改:
success: function (data, textStatus, jqXHR) {
增加体验,又调了一些前端的东东,到这里 Student 和Score两个表的增删改查都完成了,但是现在没有涉及到多表,这里能看到Score的课程名还是自己的,现在我们要抓取Course中的cname,
查了一下,MBG生成的mapper 和po 并美欧生成与多表查询有关的东西,所以这里我们需要修改一下mapper 和po才能达到多表查询的需求:
在ScMapper.xml中加入这一段:
然后在Sc的po中加上private Course course 以及getset方法。
单元测试一下,通过,然后写到程式里:
多表的查询完成了。
AOP:
现在该搞搞AOP了,在这里业务上就假设insert update 和delete的操作要写个log吧。
这里先搭一下AOP:
java.lang.NoClassDefFoundError: org/aopalliance/aop/Advice
deploy的时候报出了这个错误,百度了一下,发现少了个必不可少的包:
在公司用了快2年的EJB和structs2了,大半个月前开始入手ssm,mybatis入门还算简单,经过一周的空闲时间学习,就能写mapper 增删改查,也知道能够用逆向工程MBG根据table构建po 和mapper,:
MBG构建的po与mapper |
整合到spring让spring代理mybatis也ok:
嗯~可以run
测试Mapper |
applicationContext.xml |
公司用的工具是netbeans,所以就用netbeans创建java web工程,刚创建的javaweb项目是可以run的,但是applicationContext.xml 和dispatcher-servlet.xml我不想放在web的文件夹下了,想放到classpath下并进行分类整理/config/spring /config/mybatis ,放了applicationContext.xml之后,在web.xml 修改如下
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
把dispatcher-servlet.xml放到/config/spring 下,修改配置如下:
web.xml |
run欢迎页index.jsp没问题
但是发送请求的时候,tomcat就报这个错:
No mapping found for HTTP request with URI [/SpringMybatisDemo/test.do] in DispatcherServlet with name 'dispatcher'
根据字面意思,request 在dispatcher这个servlet找不到mapping
根据字面意思,request 在dispatcher这个servlet找不到mapping
心里问:“搞什么,我不是在web.xml配置了么?”
然后答:“呵呵,你配置了controller了么?”
在dispatcher-servlet.xml增加一个bean(controller)
<!--controller-->
<!--controller-->
<bean name="/test" class="demo.controller.TestController"></bean>
public class TestController extends AbstractController {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView("test");
modelAndView.addObject("message", "hello world");
return modelAndView;
}
}
test.jsp是这样的:
<body>
<h1>TEST OK!</h1>
<h1>${message}</h1>
</body>
就能看到这个信息了:
TEST OK!
hello world
吼,已经可以正确收发请求了,那么现在开始搞SSM。
先搞搞student的信息吧,写一个student的controller,继承AbstractController
现在这个StudentMapper已经由spring代理了,但是我还是要获得spring的context才能获得StudentMapper的对象,于是写了个StudentService专门为StudentController服务:
StudentService.java |
StudentService的上层是DefaultService:
DefaultService.java |
既然想到就开始干吧,于是被一堆错误卡了很久......
后来重新换了个tomcat
在网上查了一下,很多推荐用SpringContextUtil implements ApplicationContextAware,这里我也用这种方法吧:
SpringContextUtil.java |
<bean id="springContextUtil" class="demo.anderson.util.SpringContextUtil" />
<bean id="studentService" class="demo.anderson.service.StudentService">
在StudentController中也要注入studentService:
<bean name="/student" class="demo.controller.StudentController">
<property name="studentService">
<ref bean="studentService"/>
</property>
</bean>
在StudentController.java中加入StudentService注入口:
StudentService studentService;
StudentService studentService;
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
student.jsp:
<body>
<title>Student</title>
<h1>Student</h1>
<h1>${student.sno}||${student.sname}${student.sage}</h1>
</body>
Student
s001||张三||23
CRUD,我们继续来进行其他的操作,要进行操作,就得有个操作界面吧。
嗯,虽然是简陋了一点,但是至少是可以操作的了:
student.jsp
|
为了有更好的用户体验,那编辑的动作我们就不跳转吧,我让编辑这个操作映射回StudentController里的不同方法,用@RequestMapping是可以做到,但是用xml怎么配置呢?上网找找看。
。。。。。。
找是找到了,旧版本的spring是哦那个MultiActionController 实现多动作controller的,有两种方法,一种是controller 继承MultiActionController ,另一种是定义代理对象,使用delegate参数指定controller。 现在感觉是挺麻烦的,所以这里我们还是哦那个@RequestMapping的方法吧
既然要用注解,那就得告诉spring我现在不单单在xml里面配置了吧,还得找一下注解的配置
开启注解:
<mvc:annotation-driven/> //如果不开启注解驱动,注解就无效了
那么既然用了@RequestMapping注解了,我们的Controller也用注解的方式吧,不然同一个bean同时存在注解与xml配置看起来不爽维护起来还怪。
用注解方式把一个controller加入bean的方式是在该controller类的上方写上@Controller
要想这个controller bean起作用,还要告诉spring我有这个bean:
那么这个时候,@Controller的注解是不是就相当于替代掉下面这一段xml配置了呢?
还差一个property,现在配置的这个StudentController没有注入StudentService,这个时候需要用到注解@Autowired
@Autowired
StudentService studentService;
通过@Controller 和@Autowired 就摆脱了在xml配置<bean>和<property>的繁琐步骤了,其实在现在已经是spring4.3的版本了,完全可以用全注解配置取代xml配置,但是还是一步步来吧。
找是找到了,旧版本的spring是哦那个MultiActionController 实现多动作controller的,有两种方法,一种是controller 继承MultiActionController ,另一种是定义代理对象,使用delegate参数指定controller。 现在感觉是挺麻烦的,所以这里我们还是哦那个@RequestMapping的方法吧
既然要用注解,那就得告诉spring我现在不单单在xml里面配置了吧,还得找一下注解的配置
开启注解:
<mvc:annotation-driven/> //如果不开启注解驱动,注解就无效了
那么既然用了@RequestMapping注解了,我们的Controller也用注解的方式吧,不然同一个bean同时存在注解与xml配置看起来不爽维护起来还怪。
用注解方式把一个controller加入bean的方式是在该controller类的上方写上@Controller
import org.springframework.stereotype.Controller;
@Controller
public class StudentController {
...
}
<context:component-scan base-package="demo.anderson.controller" />
<!--controller-->
<!-- <bean class="demo.controller.StudentController" >
<property name="studentService">
<ref bean="studentService"/>
</property>
</bean>-->
@Autowired
StudentService studentService;
StudentController.java |
通过@Controller 和@Autowired 就摆脱了在xml配置<bean>和<property>的繁琐步骤了,其实在现在已经是spring4.3的版本了,完全可以用全注解配置取代xml配置,但是还是一步步来吧。
我们来run一下:
student.jsp |
ok,现在可以继续做“编辑”操作了,用同一个controller。
不跳转,自然想到用ajax,这里我们没有用表单,所以就用超链接的方式,以GET的方法把参数放在 url里。
$.ajax({
url: "./findStudentBySno.do?sno=" + sno,
success: function (data, textStatus, jqXHR) {
alert(data);
}
});
后台用@RequestParam获取 前端通过请求url 传过来的参数,并包装成JSONObject 回写到页面中:
@RequestMapping("/findStudentBySno")
public void findStudentBySno(
HttpServletRequest request,
HttpServletResponse response,
@RequestParam("sno") String sno) throws IOException {
Student student = studentService.findStudentBySno(sno);
JSONObject jSONObject = new JSONObject(student);
response.getWriter().write(jSONObject.toString());
}
前端获取到json数据并对json数据进行解析:
student.jsp |
student.jsp |
现在是可以达到大概的效果了,可是到前端的中文都变成了问号“?”,这应该是编码不一致导致的,后台调一下编码试试。
response.setCharacterEncoding("UTF-8");
在StudentController中加上这段代码,前端的中文就可以正常显示了。
但如果每个controller都要加,代码就重复了,能不能在更外层的地方,只设置一次,之后就不用再设置了呢?
spring这里也为开发者提供了方法:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
forceEncoding == false:
request.setCharacterEncoding("UTF-8");
forceEncoding == true:
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
后台传到前台的数据是json格式,那前台传到后台的格式也用json好了:
function doSubmit() {
$.ajax({
url: "./updateStudentBySno.do?sno=" + sno,
type: 'POST',
data: {
sname: $("#sname").val(),
ssex: $("#ssex").val(),
sage: $("#sage").val(),
sno: $("#sno").val()
},
dataType: 'json',
success: function (data, textStatus, jqXHR) {
alert(data);
}
});
}
<bean id="studentService" class="demo.anderson.service.StudentService">
<property name="studentMapper" ref="studentMapper"/>
</bean>
<bean id="studentMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="demo.anderson.mapper.StudentMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
public void setStudentMapper(StudentMapper studentMapper) {
this.studentMapper = studentMapper;
}
public int updateStudentBySno(Student student) {
return studentMapper.updateByPrimaryKey(student);
}
@RequestMapping("/updateStudentBySno")
public void updateStudentBySno(HttpServletRequest request,
HttpServletResponse response,
@RequestParam("sno") String sno) throws IOException {
HashMap<String, String> hm = new HashMap();
HashMap<String, String[]> hm2 = (HashMap) request.getParameterMap();
for (String key : hm2.keySet()) {
hm.put(key, hm2.get(key)[0]);
}
Student student = new Student();
student.setSage(Short.parseShort(hm.get("sage")));
student.setSname(hm.get("sname"));
student.setSno(hm.get("sno"));
student.setSsex(hm.get("ssex"));
int result = studentService.updateStudentBySno(student);
response.getWriter().write(result);
}
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:defaultAutoCommit="true" />
CRUD,还差增删,那我们就搞一搞增加一个学生:
新增学生和更新学生的操作是一样的,用的是同一个form。
删除一个学生:
function deleteStudentBySno(sno) {
var is_delete = confirm("是否要删除该学生? " + sno);
if (is_delete == true) {
$.ajax({
url: "./deleteStudentBySno.do?sno=" + sno,
success: function (data, textStatus, jqXHR) {
location.reload();
alert(data);
},
error: function (jqXHR, textStatus, errorThrown) {
alert("error:" + jqXHR + textStatus + errorThrown);
}
});
}
}
到这里,单表的增删改查就完成了,接下来要进行两张表的操作。
这里被MBG 生成的一个Key类搞了一段时间,我发现其中一个关联table的po类并没有key类,后来又去生成一次,仔细观察MBG日志才发现,在table里面的不同用户下还有一个同名的table,原来如此,drop掉其他用户的table,继续。
发现界面有点不好看,加了些bootstrap的样式:
先把Score的增删改查做一下:
由于Score是load出来的,为了增加用户体验,在编辑完成之后希望直接看到更改:
success: function (data, textStatus, jqXHR) {
window.opener.$("#load_div").load("../sc/findScoreBySno.do?sno=" + $("#sno").val() + "#sc_table", function () {
alert(data);
window.close();
});
查了一下,MBG生成的mapper 和po 并美欧生成与多表查询有关的东西,所以这里我们需要修改一下mapper 和po才能达到多表查询的需求:
在ScMapper.xml中加入这一段:
<resultMap id="ScAndCourseMap" type="demo.anderson.po.Sc">
<id column="SNO" jdbcType="VARCHAR" property="sno" />
<id column="CNO" jdbcType="VARCHAR" property="cno" />
<result column="SCORE" jdbcType="DECIMAL" property="score" />
<collection property="course" ofType="demo.anderson.po.Course">
<id column="TNO" jdbcType="VARCHAR" property="tno" />
<id column="COURSE_CNO" jdbcType="VARCHAR" property="cno" />
<result column="CNAME" jdbcType="VARCHAR" property="cname"></result>
</collection>
</resultMap>
<select id="selectScAndCourse" parameterType="demo.anderson.po.ScKey" resultMap="ScAndCourseMap">
select a.sno, a.cno, a.score, b.cno course_no, b.cname, b.tno
from sc a, course b
<where>
a.cno = b.cno
<if test="sno != null">
and a.sno = #{sno}
</if>
</where>
</select>
单元测试一下,通过,然后写到程式里:
多表的查询完成了。
AOP:
现在该搞搞AOP了,在这里业务上就假设insert update 和delete的操作要写个log吧。
这里先搭一下AOP:
java.lang.NoClassDefFoundError: org/aopalliance/aop/Advice
deploy的时候报出了这个错误,百度了一下,发现少了个必不可少的包:
Alliance.jar
为什么必不可少spring不整合到自己的jar包里面啊?
一直遇到缺少包的问题,都一一补上,这里也补一下AOP的配置吧:
<bean id="logHandler" class="demo.anderson.aop.LogHandler"></bean>
<aop:config>
<aop:aspect id="log" ref="logHandler">
<aop:pointcut id="addLog" expression="execution( * demo.anderson.controller.StudentController.*(..))" />
<aop:before method="beforeLog" pointcut-ref="addLog" />
<aop:after method="afterLog" pointcut-ref="addLog" />
</aop:aspect>
</aop:config>
------------------------------------------------------------------------------------------------------
package demo.anderson.aop;
/**
* @author anderson
*/
public class LogHandler {
public void beforeLog() {
System.out.println("-----------------------------------beforeLog()-----------------------------------");
}
public void afterLog() {
System.out.println("-----------------------------------afterLog()-----------------------------------");
}
}
配置到这里,就能实现StudentController的所有操作,都会输出:
但是如果我想配置整个Controller包下的所有方法呢?
<aop:pointcut id="addLog" expression="execution(* demo.anderson.controller.*.*(..))" />
按照正常逻辑当然是这么配置,但实际上,这样配置就报错了,具体原因不明,已经在论坛上提问了,但是这里这么配置是没问题的:
如果想获得切点方法的参数,在xml配置可以这么加:
用JoinPoint jp来获取参数:
<aop:pointcut id="addLog" expression="execution(* demo.anderson.controller.StudentController.*(..)) || execution(* demo.anderson.controller.ScoreController.*(..))" />
<aop:pointcut id="addLog" expression="execution(* demo.anderson.controller.StudentController.*(..)) || execution(* demo.anderson.controller.ScoreController.*(..)) and args(..)" />
public void beforeLog(JoinPoint jp) {
Object[] args = jp.getArgs();
for (Object obj : args) {
if (obj instanceof String) {
System.out.println("obj:" + obj);
}
}
System.out.println("-----------------------------------beforeLog()-----------------------------------");
}
obj:s001
-----------------------------------beforeLog()-----------------------------------
DEBUG [http-nio-8090-exec-115] - Creating a new SqlSession
DEBUG [http-nio-8090-exec-115] - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@49771d96] was not registered for synchronization because synchronization is not active
DEBUG [http-nio-8090-exec-115] - Fetching JDBC Connection from DataSource
DEBUG [http-nio-8090-exec-115] - JDBC Connection [jdbc:oracle:thin:@10.5.132.150:1521:whi2, UserName=WHSD, Oracle JDBC driver] will not be managed by Spring
DEBUG [http-nio-8090-exec-115] - ==> Preparing: select SNO, SNAME, SAGE, SSEX from STUDENT where SNO = ?
DEBUG [http-nio-8090-exec-115] - ==> Parameters: s001(String)
DEBUG [http-nio-8090-exec-115] - <== Total: 1
DEBUG [http-nio-8090-exec-115] - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@49771d96]
DEBUG [http-nio-8090-exec-115] - Returning JDBC Connection to DataSource
DEBUG [http-nio-8090-exec-115] - Returning cached instance of singleton bean 'logHandler'
-----------------------------------afterLog()-----------------------------------
评论
发表评论