如何优雅的使用mybatis如何使用

MyBatis如何使用(一)
作者:迷茫中守候
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了MyBatis如何使用(一)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
mybatis作为ORM轻量级框架一出现就吸引了无数人的眼球,比hibernate要简单且入门较容易,下面开始我的第一个mybatis程序。
一、下载mybatis的包
我们知道任何一个框架都会有其包,我们从其官方网站下载其包,官网网址为:http://www.mybatis.org/mybatis-3/,我这里使用的版本为3.3.0。下载完成之后解压可看到如下的目录结构:
mybatis-3.3.0.jar是其包,lib目录下是其依赖包,我们把这些包放到我们的项目中。我这里创建的是javaweb项目,方便以后做web测试,编写的程序是普通的java程序。
二、配置环境
把mybatis的包放到项目的lib目录下之后,接下来时配置mybatis的环境,我们知道mybatis做为ORM框架,属于开发中的DAO层,是和数据库打交道的,所以我们必须有数据,这里以mysql数据为例,具体的建库和建表这里不阐述。
在src目录下创建mybatis的配置文件,文件名称为:configuratin.xml,文件内容如下:
&?xml version="1.0" encoding="UTF-8" ?&
&!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"&
&configuration&
&!--别名--&
&typeAliases&
&typeAlias alias="Message" type=".imooc.entity.Message"/&
&/typeAliases&
&environments default="development"&
&environment id="development"&
&transactionManager type="JDBC"/&
&dataSource type="POOLED"&
&property name="driver" value="com.mysql.jdbc.Driver"/&
&property name="url" value="jdbc:mysql://127.0.0.1:3306/weixin?useUnicode=true&characterEncoding=UTF-8" /&
&property name="username" value="root"/&
&property name="password" value="123456"/&
&/dataSource&
&/environment&
&/environments&
&!--映射文件--&
&mapper resource="com/cn/mappers/message.xml"/&
&/mappers&
&/configuration&
在mybatis配置文件中还有很多的配置项这里仅仅使用了这几个,
&typeAliases& 别名配置,即把实体类做个别名,目的在于在映射文件中使用实体类时不使用全限类名,而是使用别名,起到简单的作用
&environments& 配置了一些环境 比如数据配置,这里我们配置了数据源
&mappers& 配置映射文件,这里配置了.mappers包下的message.xml映射文件
下面对Message实体类进行说明,此实体类里是一些属性,如下,
public class Message {
public String getId() {
public void setId(String id) {
public String getCommand() {
public void setCommand(String command) {
public String getDescription() {
public void setDescription(String description) {
this.description =
public String getComment() {
public void setComment(String comment) {
public String toString() {
return "Message [id=" + id + ", command=" + command + ", description="
+ description + ", comment=" + comment + "]";
提供了getXXX和setXXX方法,其中setXXX方法很关键,我这里的属性和数据库的字段名是一致,可以方便使用mybatis查询出结果之后反射到实体类中,当然也可以和数据库表字段名不一致,后续会进行说明。
message.xml映射文件如下,
&mapper namespace=".inter.IMessageOperation"&
&select id="selectUserByID" parameterType="int" resultType=".imooc.entity.Message"&
select * from `message` where id = #{id}
&select id="selectMessages" resultType="Message"&
select id,
description,
这是我的mapper映射文件,里边有两个方法,一个是:selectUserById 根据id查询,另一个是selectMessages 查询所有
好了,到此为止,我们的mybatis的环境搭建完成,下面可以进行测试了。
下面是测试代码,
import java.io.IOE
import java.io.R
import org.apache.ibatis.io.R
import org.apache.ibatis.session.SqlS
import org.apache.ibatis.session.SqlSessionF
import org.apache.ibatis.session.SqlSessionFactoryB
.imooc.entity.M
public class MyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
SqlSession sqlSession=
//1、获得sqlSessionFactory
reader = Resources.getResourceAsReader("Configuration.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
//2、获得sqlSession
sqlSession=sqlSessionFactory.openSession();
Message message=sqlSession.selectOne(".inter.IMessageOperation.selectUserByID", 1);
System.out.println(message);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
sqlSession.close();
从上面可以看出,首先需要一个SqlSessionFactory,然后由sqlSessionFactory获得sqlSession,由sqlSession执行查询,使用了selectOne方法,第一个参数是映射文件中的nameSpace+“.”方法名,第二个参数是查询参数。
以上所述是小编给大家介绍的MyBatis如何使用(一)的全部叙述,希望对大家有所帮助。后续还会介绍别的版本,更多内容敬请关注脚本之家!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具专栏:深入理解MyBatis原理 - 博客频道 - CSDN.NET
> & & > &深入理解MyBatis原理
深入分析MyBatis源码,全面解析MyBatis的设计架构,探究MyBatis工作原理和机制,并针对MyBatis的高级性能进行探讨,让你的MyBatis功能发挥的淋漓尽致~
本文介绍如何细粒度地控制你的MyBatis二级缓存,以及对应的mybatis-enhanced-cache插件实现
本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。本文将全面分析MyBatis的二级缓存的设计原理。
MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上。MyBatis提供了一级缓存、二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高系统的性能。本...
MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路,并且讨论MyBatis的几个核心部件,然后结合一个select查询实例,...
MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面。本文将讲述MyBatis的事务管理的实现机制。首先介绍MyBatis的事务Transaction的接口设计以及其不...
对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题。本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池。
    本文首先会...
对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。本章将通过以下几点详细介绍MyBatis的初始化过程。
1.MyBatis的初始化做了什么14033人阅读
Java(15)
我们看招聘信息的时候,经常会看到这一点,需要具备SSH框架的技能;而且在大部分教学课堂中,也会把SSH作为最核心的教学内容。
但是,我们在实际应用中发现,SpringMVC可以完全替代Struts,配合注解的方式,编程非常快捷,而且通过restful风格定义url,让地址看起来非常优雅。
另外,MyBatis也可以替换Hibernate,正因为MyBatis的半自动特点,我们程序猿可以完全掌控SQL,这会让有数据库经验的程序猿能开发出高效率的SQL语句,而且XML配置管理起来也非常方便。
好了,如果你也认同我的看法,那么下面我们一起来做整合吧!
在写代码之前我们先了解一下这三个框架分别是干什么的?
相信大以前也看过不少这些概念,我这就用大白话来讲,如果之前有了解过可以跳过这一大段,直接看代码!
SpringMVC:它用于web层,相当于controller(等价于传统的servlet和struts的action),用来处理用户请求。举个例子,用户在地址栏输入http://网站域名/login,那么springmvc就会拦截到这个请求,并且调用controller层中相应的方法,(中间可能包含验证用户名和密码的业务逻辑,以及查询数据库操作,但这些都不是springmvc的职责),最终把结果返回给用户,并且返回相应的页面(当然也可以只返回json/xml等格式数据)。springmvc就是做前面和后面过程的活,与用户打交道!!
Spring:太强大了,以至于我无法用一个词或一句话来概括它。但与我们平时开发接触最多的估计就是IOC容器,它可以装载bean(也就是我们java中的类,当然也包括service dao里面的),有了这个机制,我们就不用在每次使用这个类的时候为它初始化,很少看到关键字new。另外spring的aop,事务管理等等都是我们经常用到的。
MyBatis:如果你问我它跟鼎鼎大名的Hibernate有什么区别?我只想说,他更符合我的需求。第一,它能自由控制sql,这会让有数据库经验的人(当然不是说我啦~捂脸~)编写的代码能搞提升数据库访问的效率。第二,它可以使用xml的方式来组织管理我们的sql,因为一般程序出错很多情况下是sql出错,别人接手代码后能快速找到出错地方,甚至可以优化原来写的sql。
SSM框架整合配置
好了,前面bb那么多,下面我们真正开始敲代码了~
首先我们打开IED,我这里用的是eclipse(你们应该也是用的这个,对吗?),创建一个动态web项目,建立好相应的目录结构(重点!)
(打了马赛克是因为这里还用不到,你们不要那么污好不好?)
我说一下每个目录都有什么用吧(第一次画表格,我发现markdown的表格语法很不友好呀~)
这个目录结构同时也遵循maven的目录规范~
根目录,没什么好说的,下面有main和test。
主要目录,可以放java代码和一些资源文件。
存放我们的java代码,这个文件夹要使用Build Path -& Use as Source Folder,这样看包结构会方便很多,新建的包就相当于在这里新建文件夹咯。
存放资源文件,譬如各种的spring,mybatis,log配置文件。
存放dao中每个方法对应的sql,在这里配置,无需写daoImpl。
这里当然是存放spring相关的配置文件,有dao service web三层。
其实这个可以没有,但是为了项目完整性还是加上吧。
这个貌似是最熟悉的目录了,用来存放我们前端的静态资源,如jsp js css。
这里的资源是指项目的静态资源,如js css images等。
很重要的一个目录,外部浏览器无法访问,只有羡慕内部才能访问,可以把jsp放在这里,另外就是web.xml了。你可能有疑问了,为什么上面java中的resources里面的配置文件不妨在这里,那么是不是会被外部窃取到?你想太多了,部署时候基本上只有webapp里的会直接输出到根目录,其他都会放入WEB-INF里面,项目内部依然可以使用classpath:XXX来访问,好像IDE里可以设置部署输出目录,这里扯远了~
这里是测试分支。
测试java代码,应遵循包名相同的原则,这个文件夹同样要使用Build Path -& Use as Source Folder,这样看包结构会方便很多。
没什么好说的,好像也很少用到,但这个是maven的规范。
我先新建好几个必要的包,并为大家讲解一下每个包的作用,顺便理清一下后台的思路~
数据访问层(接口)
与数据打交道,可以是数据库操作,也可以是文件读写操作,甚至是redis缓存操作,总之与数据操作有关的都放在这里,也有人叫做dal或者数据持久层都差不多意思。为什么没有daoImpl,因为我们用的是mybatis,所以可以直接在配置文件中实现接口的每个方法。
一般与数据库的表相对应,封装dao层取出来的数据为一个对象,也就是我们常说的pojo,一般只在dao层与service层之间传输。
数据传输层
刚学框架的人可能不明白这个有什么用,其实就是用于service层与web层之间传输,为什么不直接用entity(pojo)?其实在实际开发中发现,很多时间一个entity并不能满足我们的业务需求,可能呈现给用户的信息十分之多,这时候就有了dto,也相当于vo,记住一定不要把这个混杂在entity里面,答应我好吗?
业务逻辑(接口)
写我们的业务逻辑,也有人叫bll,在设计业务接口时候应该站在“使用者”的角度。额,不要问我为什么这里没显示!IDE调皮我也拿它没办法~
serviceImpl
业务逻辑(实现)
实现我们业务接口,一般事务控制是写在这里,没什么好说的。
springmvc就是在这里发挥作用的,一般人叫做controller控制器,相当于struts中的action。
还有最后一步基础工作,导入我们相应的jar包,我使用的是maven来管理我们的jar,所以只需要在pom.xml中加入相应的依赖就好了,如果不使用maven的可以自己去官网下载相应的jar,放到项目WEB-INF/lib目录下。关于maven的学习大家可以看,这里就不展开了。我把项目用到的jar都写在下面,版本都不是最新的,大家有经验的话可以自己调整版本号。另外,所有jar都会与项目一起打包放到我的上,喜欢的给个star吧~
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&
&com.soecode.ssm&
&0.0.1-SNAPSHOT&
&ssm Maven Webapp&
&/liyifeng1994/ssm&
&ch.qos.logback&
&logback-classic&
&mysql-connector-java&
&org.mybatis&
&org.mybatis&
&mybatis-spring&
&standard&
&com.fasterxml.jackson.core&
&jackson-databind&
&javax.servlet&
&javax.servlet-api&
&org.springframework&
&spring-core&
&4.1.7.RELEASE&
&org.springframework&
&spring-beans&
&4.1.7.RELEASE&
&org.springframework&
&spring-context&
&4.1.7.RELEASE&
&org.springframework&
&spring-jdbc&
&4.1.7.RELEASE&
&org.springframework&
&spring-tx&
&4.1.7.RELEASE&
&org.springframework&
&spring-web&
&4.1.7.RELEASE&
&org.springframework&
&spring-webmvc&
&4.1.7.RELEASE&
&org.springframework&
&spring-test&
&4.1.7.RELEASE&
&redis.clients&
&com.dyuproject.protostuff&
&protostuff-core&
&com.dyuproject.protostuff&
&protostuff-runtime&
&commons-collections&
&commons-collections&
下面真的要开始进行编码工作了,坚持到这里辛苦大家了~
第一步:我们先在spring文件夹里新建spring-dao.xml文件,因为spring的配置太多,我们这里分三层,分别是dao service web。
读入数据库连接相关参数(可选)
配置数据连接池
配置连接属性,可以不读配置项文件直接在这里写死
配置c3p0,只配了几个常用的
配置SqlSessionFactory对象(mybatis)
扫描dao层接口,动态实现dao接口,也就是说不需要daoImpl,sql和参数都写在xml文件上
spring-dao.xml
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"&
location="classpath:jdbc.properties" /&
id="dataSource" class="com.mchange.boPooledDataSource"&
name="driverClass" value="${jdbc.driver}" /&
name="jdbcUrl" value="${jdbc.url}" /&
name="user" value="${jdbc.username}" /&
name="password" value="${jdbc.password}" /&
name="maxPoolSize" value="30" /&
name="minPoolSize" value="10" /&
name="autoCommitOnClose" value="false" /&
name="checkoutTimeout" value="10000" /&
name="acquireRetryAttempts" value="2" /&
id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"&
name="dataSource" ref="dataSource" /&
name="configLocation" value="classpath:mybatis-config.xml" /&
name="typeAliasesPackage" value="com.soecode.lyf.entity" /&
name="mapperLocations" value="classpath:mapper/*.xml" /&
class="org.mybatis.spring.mapper.MapperScannerConfigurer"&
name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /&
name="basePackage" value="com.soecode.lyf.dao" /&
因为数据库配置相关参数是读取配置文件,所以在resources文件夹里新建一个jdbc.properties文件,存放我们4个最常见的数据库连接属性,这是我本地的,大家记得修改呀~还有喜欢传到github上“大头虾们”记得删掉密码,不然别人就很容易得到你服务器的数据库配置信息,然后干一些羞羞的事情,你懂的!!
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=
友情提示:配置文件中的jdbc.username,如果写成username,可能会与系统环境中的username变量冲突,所以到时候真正连接数据库的时候,用户名就被替换成系统中的用户名(有得可能是administrator),那肯定是连接不成功的,这里有个小坑,我被坑了一晚上!!
因为这里用到了mybatis,所以需要配置mybatis核心文件,在recources文件夹里新建mybatis-config.xml文件。
使用自增主键
使用列别名
开启驼峰命名转换 create_time -& createTime
mybatis-config.xml
&?xml version="1.0" encoding="UTF-8" ?&
&!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"&
name="useGeneratedKeys" value="true" /&
name="useColumnLabel" value="true" /&
name="mapUnderscoreToCamelCase" value="true" /&
第二步:刚弄好dao层,接下来到service层了。在spring文件夹里新建spring-service.xml文件。
扫描service包所有注解 @Service
配置事务管理器,把事务管理交由spring来完成
配置基于注解的声明式事务,可以直接在方法上@Transaction
spring-service.xml
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"&
base-package="com.soecode.lyf.service" /&
id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&
name="dataSource" ref="dataSource" /&
transaction-manager="transactionManager" /&
第三步:配置web层,在spring文件夹里新建spring-web.xml文件。
开启SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
对静态资源处理,如js,css,jpg等
配置jsp 显示ViewResolver,例如在controller中某个方法返回一个string类型的”login”,实际上会返回”/WEB-INF/login.jsp”
扫描web层 @Controller
spring-web.xml
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"&
class="org.springframework.web.servlet.view.InternalResourceViewResolver"&
name="viewClass" value="org.springframework.web.servlet.view.JstlView" /&
name="prefix" value="/WEB-INF/jsp/" /&
name="suffix" value=".jsp" /&
base-package="com.soecode.lyf.web" /&
第四步:最后就是修改web.xml文件了,它在webapp的WEB-INF下。
xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true"&
&seckill-dispatcher&
&org.springframework.web.servlet.DispatcherServlet&
&contextConfigLocation&
&classpath:spring/spring-*.xml&
&seckill-dispatcher&
我们在项目中经常会使用到日志,所以这里还有配置日志xml,在resources文件夹里新建logback.xml文件,所给出的日志输出格式也是最基本的控制台s呼出,大家有兴趣查看。
logback.xml
&?xml version="1.0" encoding="UTF-8"?&
debug="true"&
name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&
&%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&
level="debug"&
ref="STDOUT" /&
到目前为止,我们一共写了7个配置文件,我们一起来看下最终的配置文件结构图。
SSM框架应用实例(图书管理系统)
一开始想就这样结束教程,但是发现其实很多人都还不会把这个SSM框架用起来,特别是mybatis部分。那我现在就以最常见的“图书管理系统”中【查询图书】和【预约图书】业务来做一个demo吧!
首先新建数据库名为ssm,再创建两张表:图书表book和预约图书表appointment,并且为book表初始化一些数据,sql如下。
schema.sql
CREATE TABLE `book` (
`book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',
`name` varchar(100) NOT NULL COMMENT '图书名称',
`number` int(11) NOT NULL COMMENT '馆藏数量',
PRIMARY KEY (`book_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='图书表'
-- 初始化图书数据
INSERT INTO `book` (`book_id`, `name`, `number`)
(1000, 'Java程序设计', 10),
(1001, '数据结构', 10),
(1002, '设计模式', 10),
(1003, '编译原理', 10)
-- 创建预约图书表
CREATE TABLE `appointment` (
`book_id` bigint(20) NOT NULL COMMENT '图书ID',
`student_id` bigint(20) NOT NULL COMMENT '学号',
`appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '预约时间' ,
PRIMARY KEY (`book_id`, `student_id`),
INDEX `idx_appoint_time` (`appoint_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='预约图书表'
在entity包中添加两个对应的实体,图书实体Book.java和预约图书实体Appointment.java。
package com.soecode.lyf.
public class Book {
private long bookId;
private int
Appointment.java
package com.soecode.lyf.
import java.util.D
* 预约图书实体
public class Appointment {
private long bookId;
private long studentId;
private Date appointT
在dao包新建接口BookDao.java和Appointment.java
BookDao.java
package com.soecode.lyf.
import java.util.L
import com.soecode.lyf.entity.B
public interface BookDao {
* 通过ID查询单本图书
Book queryById(long id);
* 查询所有图书
* offset 查询起始位置
* limit 查询条数
List&Book& queryAll(@Param("offset") int offset, @Param("limit") int limit);
* 减少馆藏数量
* 如果影响行数等于&1,表示更新的记录行数
int reduceNumber(long bookId);
AppointmentDao.java
package com.soecode.lyf.
import org.apache.ibatis.annotations.P
import com.soecode.lyf.entity.A
public interface AppointmentDao {
* 插入预约图书记录
* studentId
* 插入的行数
int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);
* 通过主键查询预约图书记录,并且携带图书实体
* studentId
Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId);
提示:这里为什么要给方法的参数添加@Param注解呢?是因为该方法有两个或以上的参数,一定要加,不然mybatis识别不了。上面的BookDao接口的queryById方法和reduceNumber方法只有一个参数book_id,所以可以不用加 @Param注解,当然加了也无所谓~
注意,这里不需要实现dao接口不用编写daoImpl, mybatis会给我们动态实现,但是我们需要编写相应的mapper。
在mapper目录里新建两个文件BookDao.xml和AppointmentDao.xml,分别对应上面两个dao接口,代码如下。
BookDao.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"&
namespace="com.soecode.lyf.dao.BookDao"&
id="queryById" resultType="Book" parameterType="long"&
book_id = #{bookId}
id="queryAll" resultType="Book"&
LIMIT #{offset}, #{limit}
id="reduceNumber"&
UPDATE book
SET number = number - 1
book_id = #{bookId}
AND number & 0
AppointmentDao.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"&
namespace="com.soecode.lyf.dao.AppointmentDao"&
id="insertAppointment"&
INSERT ignore INTO appointment (book_id, student_id)
VALUES (#{bookId}, #{studentId})
id="queryByKeyWithBook" resultType="Appointment"&
a.book_id,
a.student_id,
a.appoint_time,
b.book_id "book.book_id",
b.`name` "book.name",
b.number "book.number"
appointment a
INNER JOIN book b ON a.book_id = b.book_id
a.book_id = #{bookId}
AND a.student_id = #{studentId}
mapper总结:namespace是该xml对应的接口全名,select和update中的id对应方法名,resultType是返回值类型,parameterType是参数类型(这个其实可选),最后#{...}中填写的是方法的参数,看懂了是不是很简单!!我也这么觉得~ 还有一个小技巧要交给大家,就是在返回Appointment对象包含了一个属性名为book的Book对象,那么可以使用"book.属性名"的方式来取值,看上面queryByKeyWithBook方法的sql。
dao层写完了,接下来test对应的package写我们测试方法吧。
因为我们之后会写很多测试方法,在测试前需要让程序读入spring-dao和mybatis等配置文件,所以我这里就抽离出来一个BaseTest类,只要是测试方法就继承它,这样那些繁琐的重复的代码就不用写那么多了~
BaseTest.java
package com.soecode.
import org.junit.runner.RunW
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.SpringJUnit4ClassR
* 配置spring和junit整合,junit启动时加载springIOC容器 spring-test,junit
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml" })
public class BaseTest {
因为spring-service在service层的测试中会时候到,这里也一起引入算了!
新建BookDaoTest.java和AppointmentDaoTest.java两个dao测试文件。
BookDaoTest.java
package com.soecode.lyf.
import java.util.L
import org.junit.T
import org.springframework.beans.factory.annotation.A
import com.soecode.lyf.BaseT
import com.soecode.lyf.entity.B
public class BookDaoTest extends BaseTest {
@Autowired
private BookDao bookD
public void testQueryById() throws Exception {
long bookId = 1000;
Book book = bookDao.queryById(bookId);
System.out.println(book);
public void testQueryAll() throws Exception {
List&Book& books = bookDao.queryAll(0, 4);
for (Book book : books) {
System.out.println(book);
public void testReduceNumber() throws Exception {
long bookId = 1000;
int update = bookDao.reduceNumber(bookId);
System.out.println("update=" + update);
BookDaoTest测试结果
testQueryById
testQueryAll
testReduceNumber
AppointmentDaoTest.java
package com.soecode.lyf.
import org.junit.T
import org.springframework.beans.factory.annotation.A
import com.soecode.lyf.BaseT
import com.soecode.lyf.entity.A
public class AppointmentDaoTest extends BaseTest {
@Autowired
private AppointmentDao appointmentD
public void testInsertAppointment() throws Exception {
long bookId = 1000;
long studentId = L;
int insert = appointmentDao.insertAppointment(bookId, studentId);
System.out.println("insert=" + insert);
public void testQueryByKeyWithBook() throws Exception {
long bookId = 1000;
long studentId = L;
Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);
System.out.println(appointment);
System.out.println(appointment.getBook());
AppointmentDaoTest测试结果
testInsertAppointment
testQueryByKeyWithBook
嗯,到这里一切到很顺利~那么我们继续service层的编码吧~可能下面开始信息里比较大,大家要做好心理准备~
首先,在写我们的控制器之前,我们先定义几个预约图书操作返回码的数据字典,也就是我们要返回给客户端的信息。我们这类使用枚举类,没听过的小伙伴要好好恶补一下了(我也是最近才学到的= =)
预约业务操作返回码说明
新建一个包叫enums,在里面新建一个枚举类AppointStateEnum.java,用来定义预约业务的数据字典,没听懂没关系,我们直接看代码吧~是不是感觉有模有样了!
AppointStateEnum.java
package com.soecode.lyf.
* 使用枚举表述常量数据字典
public enum AppointStateEnum {
SUCCESS(1, "预约成功"), NO_NUMBER(0, "库存不足"), REPEAT_APPOINT(-1, "重复预约"), INNER_ERROR(-2, "系统异常");
private int
private String stateI
private AppointStateEnum(int state, String stateInfo) {
this.state =
this.stateInfo = stateI
public int getState() {
public String getStateInfo() {
return stateI
public static AppointStateEnum stateOf(int index) {
for (AppointStateEnum state : values()) {
if (state.getState() == index) {
return null;
接下来,在dto包下新建AppointExecution.java用来存储我们执行预约操作的返回结果。
AppointExecution.java
package com.soecode.lyf.
import com.soecode.lyf.entity.A
import com.soecode.lyf.enums.AppointStateE
* 封装预约执行后结果
public class AppointExecution {
private long bookId;
private int
private String stateI
private App
public AppointExecution() {
public AppointExecution(long bookId, AppointStateEnum stateEnum) {
this.bookId = bookId;
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
public AppointExecution(long bookId, AppointStateEnum stateEnum, Appointment appointment) {
this.bookId = bookId;
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.appointment =
接着,在exception包下新建三个文件
NoNumberException.java
RepeatAppointException.java
AppointException.java
预约业务异常类(都需要继承RuntimeException),分别是无库存异常、重复预约异常、预约未知错误异常,用于业务层非成功情况下的返回(即成功返回结果,失败抛出异常)。
NoNumberException.java
package com.soecode.lyf.
* 库存不足异常
public class NoNumberException extends RuntimeException {
public NoNumberException(String message) {
super(message);
public NoNumberException(String message, Throwable cause) {
super(message, cause);
RepeatAppointException.java
package com.soecode.lyf.
* 重复预约异常
public class RepeatAppointException extends RuntimeException {
public RepeatAppointException(String message) {
super(message);
public RepeatAppointException(String message, Throwable cause) {
super(message, cause);
AppointException.java
package com.soecode.lyf.
* 预约业务异常
public class AppointException extends RuntimeException {
public AppointException(String message) {
super(message);
public AppointException(String message, Throwable cause) {
super(message, cause);
咱们终于可以编写业务代码了,在service包下新建BookService.java图书业务接口。
BookService.java
package com.soecode.lyf.
import java.util.L
import com.soecode.lyf.dto.AppointE
import com.soecode.lyf.entity.B
* 业务接口:站在"使用者"角度设计接口 三个方面:方法定义粒度,参数,返回类型(return 类型/异常)
public interface BookService {
* 查询一本图书
Book getById(long bookId);
* 查询所有图书
List&Book& getList();
* 预约图书
* studentId
AppointExecution appoint(long bookId, long studentId);
在service.impl包下新建BookServiceImpl.java使用BookService接口,并实现里面的方法。
BookServiceImpl
package com.soecode.lyf.service.
import java.util.L
import org.slf4j.L
import org.slf4j.LoggerF
import org.springframework.beans.factory.annotation.A
import org.springframework.stereotype.S
import org.springframework.transaction.annotation.T
import com.soecode.lyf.dao.AppointmentD
import com.soecode.lyf.dao.BookD
import com.soecode.lyf.dto.AppointE
import com.soecode.lyf.entity.A
import com.soecode.lyf.entity.B
import com.soecode.lyf.enums.AppointStateE
import com.soecode.lyf.exception.AppointE
import com.soecode.lyf.exception.NoNumberE
import com.soecode.lyf.exception.RepeatAppointE
import com.soecode.lyf.service.BookS
public class BookServiceImpl implements BookService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private BookDao bookD
@Autowired
private AppointmentDao appointmentD
public Book getById(long bookId) {
return bookDao.queryById(bookId);
public List&Book& getList() {
return bookDao.queryAll(0, 1000);
@Transactional
* 使用注解控制事务方法的优点: 1.开发团队达成一致约定,明确标注事务方法的编程风格
* 2.保证事务方法的执行时间尽可能短,不要穿插其他网络操作,RPC/HTTP请求或者剥离到事务方法外部
* 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制
public AppointExecution appoint(long bookId, long studentId) {
int update = bookDao.reduceNumber(bookId);
if (update &= 0) {
throw new NoNumberException("no number");
int insert = appointmentDao.insertAppointment(bookId, studentId);
if (insert &= 0) {
throw new RepeatAppointException("repeat appoint");
Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);
return new AppointExecution(bookId, AppointStateEnum.SUCCESS, appointment);
} catch (NoNumberException e1) {
} catch (RepeatAppointException e2) {
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new AppointException("appoint inner error:" + e.getMessage());
下面我们来测试一下我们的业务代码吧~因为查询图书的业务不复杂,所以这里只演示我们最重要的预约图书业务!!
BookServiceImplTest.java
package com.soecode.lyf.service.
import static org.junit.Assert.
import org.junit.T
import org.springframework.beans.factory.annotation.A
import com.soecode.lyf.BaseT
import com.soecode.lyf.dto.AppointE
import com.soecode.lyf.service.BookS
public class BookServiceImplTest extends BaseTest {
@Autowired
private BookService bookS
public void testAppoint() throws Exception {
long bookId = 1001;
long studentId = L;
AppointExecution execution = bookService.appoint(bookId, studentId);
System.out.println(execution);
BookServiceImplTest测试结果
testAppoint
首次执行是“预约成功”,如果再次执行的话,应该会出现“重复预约”,哈哈,我们所有的后台代码都通过单元测试啦~~是不是很开心~
咱们还需要在dto包里新建一个封装json返回结果的类Result.java,设计成泛型。
Result.java
package com.soecode.lyf.
* 封装json对象,所有返回结果都使用它
public class Result&T& {
private boolean
public Result() {
public Result(boolean success, T data) {
this.success =
this.data =
public Result(boolean success, String error) {
this.success =
this.error =
最后,我们写web层,也就是controller,我们在web包下新建BookController.java文件。
BookController.java
package com.soecode.lyf.
import java.util.L
import org.apache.ibatis.annotations.P
import org.slf4j.L
import org.slf4j.LoggerF
import org.springframework.beans.factory.annotation.A
import org.springframework.stereotype.C
import org.springframework.ui.M
import org.springframework.web.bind.annotation.PathV
import org.springframework.web.bind.annotation.RequestM
import org.springframework.web.bind.annotation.RequestM
import org.springframework.web.bind.annotation.ResponseB
import com.soecode.lyf.dto.AppointE
import com.soecode.lyf.dto.R
import com.soecode.lyf.entity.B
import com.soecode.lyf.enums.AppointStateE
import com.soecode.lyf.exception.NoNumberE
import com.soecode.lyf.exception.RepeatAppointE
import com.soecode.lyf.service.BookS
@Controller
@RequestMapping("/book")
public class BookController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private BookService bookS
@RequestMapping(value = "/list", method = RequestMethod.GET)
private String list(Model model) {
List&Book& list = bookService.getList();
model.addAttribute("list", list);
return "list";
@RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET)
private String detail(@PathVariable("bookId") Long bookId, Model model) {
if (bookId == null) {
return "redirect:/book/list";
Book book = bookService.getById(bookId);
if (book == null) {
return "forward:/book/list";
model.addAttribute("book", book);
return "detail";
@RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = {
"application/ charset=utf-8" })
@ResponseBody
private Result&AppointExecution& appoint(@PathVariable("bookId") Long bookId, @RequestParam("studentId") Long studentId) {
if (studentId == null || studentId.equals("")) {
return new Result&&(false, "学号不能为空");
AppointExecution execution = null;
execution = bookService.appoint(bookId, studentId);
} catch (NoNumberException e1) {
execution = new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);
} catch (RepeatAppointException e2) {
execution = new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);
} catch (Exception e) {
execution = new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);
return new Result&AppointExecution&(true, execution);
因为我比较懒,所以我们就不测试controller了,好讨厌写前端,呜呜呜~
到此,我们的SSM框架整合配置,与应用实例部分已经结束了,我把所有源码和jar包一起打包放在了我的GitHub上,需要的可以去下载,喜欢就给个star吧,这篇东西写了两个晚上也不容易啊。
更新(感谢网友EchoXml发现):
修改预约业务代码,失败时抛异常,成功时才返回结果,控制层根据捕获的异常返回相应信息给客户端,而不是业务层直接返回错误结果。上面的代码已经作了修改,而且错误示范也注释保留着,之前误人子弟了,还好有位网友前几天提出质疑,我也及时做了修改。
更新(感谢网友ergeerge1建议):
修改BookController几处错误
1.detail方法不是返回json的,故不用加@ResponseBody注解
2.appoint方法应该加上@ResponseBody注解
3.另外studentId参数注解应该是@RequestParam
4.至于controller测试,测试appoint方法可不必写jsp,用curl就行,比如
curl -H “Accept: application/ charset=utf-8” -d “studentId=” localhost:8080/book/1003/appoint
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:65762次
积分:1011
积分:1011
排名:千里之外
原创:31篇
评论:46条}

我要回帖

更多关于 mybatis如何使用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信