SSM框架学习笔记5—Mybatis
SSM框架学习笔记系列,记录的是我在参加学校的京淘项目时所学习的内容,经我个人总结整理而成为本系列学习笔记。预计将分开为X篇进行记录。
接下来是这一系列的第五篇学习笔记
1 MyBatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。(来自百度百科)
1.1 与hibernate对比
业务层控制层和使用Hibernate框架一样。Hibernate基于hql是完全面向对象,全自动ORM。Mybatis基于sql是半面向对象,半自动的ORM。
1.2 内部组件
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
1.3 功能架构
我们把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
2 开发准备
2.1 引入DTD文件提示支持
通过这两个下载地址,下载这两个dtd文件,并储存于不常变化位置的地方
http://mybatis.org/dtd/mybatis-3-config.dtd
http://mybatis.org/dtd/mybatis-3-mapper.dtd
之后在eclipse中windows→preferences,按下图找到XML Catalog中的User Specified Entries,然后Add
如下面两图所示,添加刚刚下载的两个文件依赖,其中location为你储存的本地磁盘地址,而key为下载的地址
之后在编辑mybatis的config文件和mapper文件时就会有提示(虽然没有提示也没关系)
3 入门案例
需要知道mybatis支持两种sql映射方式,一种是通过xml配置文件映射方式,另一种是通过接口注解方式,接下来将分别给出两种方式的案例
3.1 数据准备
以下为一个.sql文件的内容,可复制到一个txt后改后缀,然后直接导入数据库,详细过程另查。
这个SQL文件的主要内容是建立一个数据库"mybatisdb",然后创建数据表"user"并向其中插入了一些测试数据
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatisdb` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mybatisdb`;
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`USER_NAME` varchar(30) DEFAULT NULL,
`BIRTHDAY` datetime DEFAULT NULL,
`ADDRESS` varchar(200) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
/*Data for the table `user` */
insert into `user`(`ID`,`USER_NAME`,`BIRTHDAY`,`ADDRESS`) values (1,'夏言','1573-01-01 00:00:00','桂州村'),(2,'严嵩','1587-01-01 00:00:00','分宜县城介桥村'),(3,'徐阶','1580-01-01 00:00:00','明松江府华亭县'),(4,'高拱','1566-01-01 00:00:00','河南省新郑市高老庄村'),(5,'张居正','1558-01-01 00:00:00','江陵'),(6,'tina','1980-02-03 00:00:00','北京');
3.2 XML映射方式
新建maven工程,详细过程此前讲过,此处不再赘述
3.2.1 目录结构
以下为这个测试项目的目录结构
3.2.2 pom.xml
在pom.xml文件中引入mybatis依赖和mysql数据库连接依赖
<?xml version="1.0" encoding="UTF-8"?>
<project 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>hn-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hn-mybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- mysql数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2.3 SqlMapConfig.xml
编辑核心配置文件,通过这个文件,设置数据源信息(数据库驱动、数据库连接地址、数据库账户和密码);设置基础配置(某个POJO类的别名,可以开启变量驼峰规则);设置映射文件或映射接口类的路径
xml文件是比较原始的配置文件了,现在一般使用配置更加简洁的yml文件,不过这里先介绍xml文件,以后再介绍yml文件如何配置
<?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>
<!-- 上下标签是有顺序,不能颠倒;驼峰规则默认false,修改为true -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 设置别名,之后可以在映射文件UserMapper.xml中使用到-->
<typeAliases>
<typeAlias type="cn.tedu.mybatis.pojo.User" alias="User"/>
</typeAliases>
<!-- 配置数据库环境,包括数据库驱动,地址,账号和密码。地址(端口)、账号和密码需要根据自己实际来配置 -->
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3307/mybatisdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 声明映射文件-->
<mapper resource="mybatis/mappers/UserMapper.xml"/>
</mappers>
</configuration>
3.2.4 User.java
这个文件是一个POJO类,对应于数据库中的User表及其字段名,从而形成正确映射。注意最好要用驼峰式规则来对应命名!比如说如果数据库中的字段名为user_name,则此处User的属性名应该对应为userName
package cn.tedu.mybatis.pojo;
import java.util.Date;
/**
* @description 描述:POJO类,要求:1)一堆私有属性,2)get/set方法,3)toString打印
* 封装数据库中数据
*/
public class User { //数据库表user表
//字段(全大写全小写,多个单词以下划线隔开)和属性(驼峰规则)对应,类型
private Integer id;
private String userName;
private Date birthday;
private String address;
//各个属性的getter和setter,以后使用lombok插件后将省去
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", birthday=" + birthday + ", address=" + address + "]";
}
}
3.2.5 UserMapper.xml
这个文件是用来写sql语句的映射文件的,通过这个文件能够定义一些sql语句,以后需要使用时可以直接调用
<?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唯一标识这个mapper,非常重要! -->
<mapper namespace="cn.tedu.mybatis.mapper.UserMapper">
<!-- 查询所有,id非常重要!唯一标识这个sql语句映射;resultType是返回值类型,parameterType是接收参数类型 -->
<select id="find" resultType="User" parameterType="User">
SELECT * FROM USER
<!-- where标签内部可以拼接多个if标签来表示条件,如果其中一个条件不符合,则不会拼接这个条件的语句,并自动去除一些连接符,从而形成正确的sql语句操作数据库 -->
<where>
<if test="id != null">id=#{id}</if>
<if test="userName != null">AND user_name=#{userName}</if>
</where>
</select>
<!-- 查询一个信息 #{id} 参数:占位符,单个参数,对应于传入的User的id -->
<select id="get" parameterType="User" resultType="User">
SELECT * FROM USER
<where>
id = #{id}
and user_name = #{userName}
</where>
</select>
<!-- 新增,多个参数,一般使用pojo来传递参数 -->
<insert id="add" parameterType="User">
INSERT INTO USER (id,user_name,birthday,address)
VALUES(#{id}, #{userName}, #{birthday}, #{address})
</insert>
<!-- 修改,set标签的功能与where标签功能类似 -->
<update id="update" parameterType="User">
UPDATE USER
<set>
<if test="userName != null">user_name=#{userName}</if>
<if test="birthday != null">, birthday=#{birthday}</if>
<if test="address != null">, address=#{address}</if>
</set>
WHERE id=#{id}
</update>
<!-- 删除 -->
<delete id="delete" parameterType="int">
DELETE FROM USER WHERE id = #{id}
</delete>
<!-- 批量删除,ids是map的key,value集合 IN (7,8,9,20)
foreach标签用来遍历传入的数组,一半都类似于下面这么写-->
<delete id="deleteMuch" parameterType="map">
DELETE FROM USER WHERE id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
<!-- 记录总数 -->
<select id="count" resultType="int">
SELECT COUNT(*) FROM USER
</select>
</mapper>
3.2.5 TestMybatisXml.java
这个文件是测试类,测试能否正常用xml映射文件方式通过mybatis操作数据库
package test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import cn.tedu.mybatis.pojo.User;
public class TestMybatisXml {
@Test
public void find() throws IOException {
SqlSession session = getSession();
//!!!statement = namespace.id namespace和id均在UserMapper.xml中有明确的定义
String statement = "cn.tedu.mybatis.mapper.UserMapper.find";
User param = new User();
param.setId(5);
param.setUserName("张居正");
List<User> userList = session.selectList(statement, param); //底层解析UserMapper.xml获取find的标签内容:sql
//遍历数据
for(User u : userList) {
System.out.println(u); //每一条数据,调用toString()
}
}
@Test
public void get() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.get";
User param = new User();
param.setId(5);
param.setUserName("张居正");
User user = session.selectOne(statement, param);
System.out.println(user);
}
@Test
public void add() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.add";
User u = new User(); //用pojo对象传参
u.setId(9);
u.setUserName("王超");
u.setBirthday(new Date());
u.setAddress("上海");
session.insert(statement, u);
session.commit(); //事务默认不提交,需要手动提交
}
@Test
public void update() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.update";
User u = new User(); //对象的属性不设置默认null
u.setId(8);
//u.setUserName("小超超");
u.setBirthday(new Date());
u.setAddress("上海外滩");
session.update(statement, u);
session.commit();
}
@Test
public void delete() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.delete";
session.delete(statement, 6);
session.commit();
}
@Test
public void deleteMuch() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.deleteMuch";
Map<String,Object> map = new HashMap<String,Object>();
map.put("ids", new Integer[] {7,8,9,99,88} ); //整数集合作为value
session.delete(statement, map);
session.commit();
}
@Test
public void count() throws IOException {
SqlSession session = getSession();
String statement = "cn.tedu.mybatis.mapper.UserMapper.count";
int count = session.selectOne(statement);
System.out.println("记录总数:" + count);
}
//获取SqlSession对象
public SqlSession getSession() throws IOException {
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
return session;
}
}
如果上述测试类的每个函数都能正常运行并输出相应内容,则表示我们已经成功配置mybatis并能够通过xml映射方式正常使用mybatis来操作数据库
3.3 接口注解方式
本例在上面创建的项目中增加修改即可
接口注解方式即把sql语句通过注解的方式来定义,并与某个接口的方法绑定,当调用这个方法时,将会执行对应的sql语句。
当需要达到类似于xml映射方式的动态sql的效果(即根据某些值是否存在来判断要不要拼接sql语句),则需要利用script标签,具体见3.3.1中的update方法的注解
3.3.1 UserMapper.java
新建一个usermapper接口
package cn.tedu.mybatis.mappers;
import java.util.List;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import cn.tedu.mybatis.pojo.User;
//关系:<mapper namespace="cn.tedu.mybatis.mappers.UserMapper">包名,接口名
public interface UserMapper {
@Select("<script>select * from user where id=#{id} <if test='userName!=null'> and username=#{userName}</if> <if test='address!=null'> and address=#{address}</if></script>")
public List<User> find(User user);
@Insert("insert into user(id,username,birthday,address) Values(#{id},#{userName},#{birthday},#{address})")
public void insert(User user);
@Update("<script>update user " +
" <set>\r\n" +
" <if test=\"userName!=null\">username=#{userName}</if>\r\n" +
" <if test=\"address!=null\">,address=#{address}</if>\r\n" +
" <if test=\"birthday!=null\">,birthday=#{birthday}</if>\r\n" +
" </set> \r\n" +
" where id=#{id}</script>")
public void update(User user);
}
3.3.2 SqlMapConfig.xml
修改SqlMapConfig.xml文件中的映射文件为接口类(之前是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>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3307/mybatisdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 将之前使用的xml映射文件修改为刚新建的接口类路径 -->
<mappers>
<mapper class="cn.tedu.mybatis.mapper.UserMapper"/>
</mappers>
</configuration>
3.3.3 TestMybatisInterface.java
这个文件是测试类,测试能否正常用接口注解方式通过mybatis操作数据库
package test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import cn.tedu.mybatis.mappers.UserMapper;
import cn.tedu.mybatis.pojo.User;
public class TestMybatisInterface {
public SqlSession getSession() throws IOException {
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
return factory.openSession();
}
@Test
public void find() throws IOException {
SqlSession session=getSession();
UserMapper mapper=session.getMapper(UserMapper.class);
User param=new User();
param.setId(5);
/* param.setUserName("张居正"); */
/* param.setAddress("江陵"); */
List<User> userList = mapper.find(param); //底层解析UserMapper.xml获取find的标签内容:sql
//遍历数据
for(User u : userList) {
System.out.println(u); //每一条数据,调用toString()
}
}
@Test
public void update() throws IOException {
SqlSession session=getSession();
UserMapper mapper=session.getMapper(UserMapper.class);
User param=new User();
param.setId(6);
param.setUserName("yyg");
/* param.setAddress("江陵"); */
mapper.update(param); //底层解析UserMapper.xml获取find的标签内容:sql
session.commit();
}
}
如果上述测试类的测试函数都能正常运行,则表示我们已经成功配置mybatis并能够通过接口注解方式正常使用mybatis来操作数据库
4 小结
mybatis是一个不错的持久层框架,通过这个框架,可以更高效地实现对数据库的操作。
但之后还会介绍一个更加简便的框架:mybatis plus,他通过继承一个接口,甚至能做到不用写sql语句即可操作数据库,极大地提高了持久层的开发效率
目前为止这一系列已更新的学习笔记导航:
SSM框架学习笔记1——Maven
SSM框架学习笔记2——Maven安装及配置
SSM框架学习笔记3——SpringBoot介绍与使用
SSM框架学习笔记4—Junit单元测试
SSM框架学习笔记5—Mybatis
SSM框架学习笔记6—Mybatis Plus
SSM框架学习笔记7—Spring
SSM框架学习笔记8—SpringMVC背景
SSM框架学习笔记9—SpringMVC常用注解和RESTful介绍
SSM框架学习笔记10—SpringMVC请求和响应