昨天搭建了一个spring的基础开发平台,今天将在spring中集成mybatis。
既然说到数据库,那就必然要有数据库版本管理软件。我这里使用的是flywaydb,因此脚本中会附上SQL脚本。maven可以集成flyway,但是为了避免pom文件太过复杂,这里使用flywaydb的command line tool。flywaydb官网:
https://flywaydb.org/
言归正传。
首先,pom中添加连接数据库必要的依赖
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
<exclusions>
<exclusion>
<artifactId>xercesImpl</artifactId>
<groupId>xerces</groupId>
</exclusion>
<exclusion>
<artifactId>commons-collections</artifactId>
<groupId>commons-collections</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.13</version>
</dependency>
项目中使用lombok自动生成getset方法,依赖如下:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.4</version>
</dependency>
rest api所需依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.0</version>
</dependency>
resources文件夹中增加配置文件:jdbc.properties
db.host=localhost
db.name=salesVolume
db.port=3306
db.user=root
db.password=root
JSTL相关依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
spring中配置数据源:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!--suppress SpringFacetInspection -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://${db.host}:${db.port}/${db.name}?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&autoReconnect=true"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
<property name="maxActive" value="10"/>
<property name="minIdle" value="2"/>
<property name="maxWait" value="300"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:sql/*-sql.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
<constructor-arg index="1" value="BATCH"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
别忘了把service和dao放到扫描目录中
<context:component-scan base-package="sales.volume.action"/>
<context:component-scan base-package="sales.volume.service.impl"/>
<context:component-scan base-package="sales.volume.dao.impl"/>
为了同时支持rest api和原有视图配置,需要修改原有视图配置:
老的代码:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
新的代码:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1"/>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
</bean>
在web.xml中新增一个servlet mapping用于rest api,代码:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
在resource目录中新建子目录sql,新增文件demo-sql.xml,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="demoDao">
<!-- 新增记录 -->
<insert id="add" parameterType="sales.volume.entity.DemoObject" useGeneratedKeys="true" keyProperty="id">
insert into r_demoObject(name, createTime, updateTime) values(#{name}, now(), now())
</insert>
<!-- 查询单条记录 -->
<select id="get" parameterType="int" resultType="sales.volume.entity.DemoObject">
select * from r_demoObject where id = #{id}
</select>
<select id="getAll" resultType="sales.volume.entity.DemoObject">
select * from r_demoObject
</select>
<!-- 修改记录 -->
<update id="update" parameterType="sales.volume.entity.DemoObject">
update r_demoObject set name = #{name}, updateTime=now() where id=#{id}
</update>
<!-- 删除记录 -->
<delete id="delete" parameterType="int">
delete from r_demoObject where id=#{id}
</delete>
</mapper>
再附上action代码:
package sales.volume.action;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import sales.volume.entity.DemoObject;
import sales.volume.entity.StateCode;
import sales.volume.service.DemoService;
import java.util.List;
@RestController
@RequestMapping("/demo")
public class DemoAction {
@Autowired
private DemoService demoService;
@RequestMapping(value = "view/{id}", method = RequestMethod.GET)
public ModelAndView view(
@PathVariable(value = "id") int id
) {
ModelAndView mav = new ModelAndView();
mav.setViewName("viewDemo");
DemoObject demoObject = demoService.get(id);
mav.getModel().put("demoObject", demoObject);
return mav;
}
@RequestMapping(value = "viewAll", method = RequestMethod.GET)
public ModelAndView viewAll(
) {
ModelAndView mav = new ModelAndView();
mav.setViewName("viewAll");
List<DemoObject> data = demoService.getAll();
mav.getModel().put("data", data);
return mav;
}
@RequestMapping(value = "add", method = RequestMethod.POST)
public ResponseEntity<StateCode> add(
@RequestParam(value = "name", required = false) String name
) {
DemoObject demoObject = new DemoObject();
demoObject.setName(name);
demoService.add(demoObject);
return new ResponseEntity(new StateCode(StateCode.SUCCESS_CODE, StateCode.SUCCESS_MSG), HttpStatus.OK);
}
@RequestMapping(value = "delete", method = RequestMethod.POST)
public ResponseEntity<StateCode> delete(
@RequestParam(value = "id", required = true) int id
) {
demoService.delete(id);
return new ResponseEntity(new StateCode(StateCode.SUCCESS_CODE, StateCode.SUCCESS_MSG), HttpStatus.OK);
}
@RequestMapping(value = "update", method = RequestMethod.POST)
public ResponseEntity<StateCode> update(
@RequestBody DemoObject[] demoObjects
) {
for (DemoObject demoObject : demoObjects) {
demoService.update(demoObject);
}
return new ResponseEntity(new StateCode(StateCode.SUCCESS_CODE, StateCode.SUCCESS_MSG), HttpStatus.OK);
}
}
对应的前台页面代码:
demoManager.jsp
<html>
<head>
<title>demo管理</title>
<style type="text/css">
.border {
border: solid 1px #AAAAAA;
margin-bottom: 50px;
display: table;
}
</style>
<script type="text/javascript" src="js/jquery-2.2.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#addButton").click(function () {
var data = {};
data.name = $("#addName").val();
$.post("demo/add.action", data, function (data, status) {
alert(data.msg);
});
});
$("#deleteButton").click(function () {
var data = {};
data.id = $("#deleteId").val();
$.post("demo/delete.action", data, function (data, status) {
alert(data.msg);
});
});
$("#viewButton").click(function(){
var id = $("#viewId").val();
location.href = ("demo/view/" + id + ".jhtml");
});
$("#allViewButton").click(function(){
location.href = ("demo/viewAll.jhtml");
});
});
</script>
</head>
<body>
<div class="border">
<div>新增</div>
<div>
<input id="addName"/>
<br>
<input id="addButton" type="button" value="新增">
</div>
</div>
<div class="border">
<div>删除</div>
<div>
<input id="deleteId"/>
<br>
<input id="deleteButton" type="button" value="删除">
</div>
</div>
<div class="border">
<div>查看</div>
<div>
<input id="viewId"/>
<br>
<input id="viewButton" type="button" value="查看">
<input id="allViewButton" type="button" value="查看全部">
</div>
</div>
</body>
</html>
viewAll.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>View All</title>
<style type="text/css">
#data {
border: solid 1px #add9c0;
border-collapse: collapse;
}
.tdB {
border: solid 1px #add9c0;
padding: 2px 5px;
}
</style>
<script type="text/javascript" src="../js/jquery-2.2.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#save").click(function () {
var data = $("#data").find('tr');
var postData = [];
for(var i = 1; i < data.length; i++) {
var row = {};
var tr = data[i];
row.id = $(tr).children("td:eq(0)").text();
row.name = $(tr).children("td:eq(1)").children("input").val();
postData.push(row);
}
$.ajax({
type: "POST",
url: "update.action",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(postData),
dataType:"json",
error: function (xhr, state, stateCode) {
var data = $.parseJSON(xhr.responseText);
mini.MessageBox.alert(data.msg, "提示");
},
success: function (data, state, xhr) {
alert(data.msg);
}
});
});
});
</script>
</head>
<body>
<table id="data" cellspacing="0">
<tr>
<td class="tdB">ID</td>
<td class="tdB">Name</td>
<td class="tdB">Create Time</td>
<td class="tdB">Update Time</td>
</tr>
<c:forEach var="ite" items="${data}">
<tr>
<td class="tdB">${ite.id}</td>
<td class="tdB"><input value="${ite.name}"></td>
<td class="tdB">${ite.createTime}</td>
<td class="tdB">${ite.updateTime}</td>
</tr>
</c:forEach>
</table>
<input id="save" type="button" value="保存"/>
</body>
</html>
viewDemo.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Edit Demo ${demoObject.id}</title>
</head>
<body>
<div>
<p>${demoObject.id}</p>
<p>${demoObject.name}</p>
<p>${demoObject.createTime}</p>
<p>${demoObject.updateTime}</p>
</div>
</body>
</html>
注意:批量修改时,用到了一种比较一种新的请求方式:json post,就是可以将前台JSON对象直接传到后台的一种提交方式。这种请求方式的服务器端代码:
@RequestMapping(value = "update", method = RequestMethod.POST)
public ResponseEntity<StateCode> update(
@RequestBody DemoObject[] demoObjects
) {
for (DemoObject demoObject : demoObjects) {
demoService.update(demoObject);
}
return new ResponseEntity(new StateCode(StateCode.SUCCESS_CODE, StateCode.SUCCESS_MSG), HttpStatus.OK);
}
请注意参数,只能有一个参数,用@RequestBody注解修饰,必须是集合或者数组类型的参数。
对应的,前端页面将要传递到后台的JSON对象转换为字符串后,当做POST参数传递到后台,注意一定要设置contentType为 "application/json"。代码:
$.ajax({
type: "POST",
url: "update.action",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(postData),
dataType:"json",
error: function (xhr, state, stateCode) {
var data = $.parseJSON(xhr.responseText);
mini.MessageBox.alert(data.msg, "提示");
},
success: function (data, state, xhr) {
alert(data.msg);
}
});
注意:JSON.stringify(postData)可以将postData对象转换为JSON字符串。chrome支持此function,IE不支持。其它浏览器自行探索。另外,可在页面中引用 json2.js(http://json.bloople.net/)来解决此兼容性问题。
实现效果:
在demoManager页面中的新增栏目下,输入框中输入姓名,能正确添加到数据库。
在删除跟查看栏目下,输入被删除/查看对象的ID,可以删除/浏览对象内容。
点击查看全部,可以看到一个列表,编辑名称,单击保存可以修改到数据库。
tips:mybatis中$和#的区别:
MyBatis/Ibatis中#和$的区别
1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".
2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
3. #方式能够很大程度防止sql注入。
4.$方式无法防止Sql注入。
5.$方式一般用于传入数据库对象,例如传入表名.
6.一般能用#的就别用$.