SSM框架学习——Mybatis
一.Mybatis
1.1 Mybatis简介
Mybatis支持SQL查询、存储过程和高级映射的持久层框架,MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果检索,Mybatis使用简单的XML或注解用于配置和映射,将接口和java的pojo 映射成数据库记录。
1.2 Mybatis功能层次结构
- API接口层:提供外部使用的接口API,开发人员通过这些API来操作数据库,接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
- 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理。它主要的目的是根据调用请求完成一次数据库的操作。
- 基础支撑层:负责最基本的功能支撑,包括连接管理、事务管理、配置加载和缓存处理等,将他们抽取出来作为最基本的组件,为上层的数据处理层提供最基础的支撑。
1.3 Mybatis核心部件
核心部件 | 作用 |
---|---|
Configuration | Mybatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部门配置都会存储在改类中 |
SqlSession | 作为Mybatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库的增删查改功能 |
Executor | Mybatis执行器,是Mybatis调度核心,负责SQL语句的生成和查询缓存的维护,由它来实现真正对数据库是操作 |
StatementHandler | 封装了JDBC Statement操作,负责对JDBC Statement的操作,如参数设置等 |
ParamentHnadler | 负责对用户传递到参数转换成JDBC Statement所对应的数据类型 |
ResultHandler | 负责将JDBC返回的ResultSet结果集对象转换成List类型集合 |
Type Handler | 负责java数据类型和jdbc数据类型之间的转化和映射 |
MappedStatement | 维护一条select、update、delete、insert节点的封装 |
1.4 Mybatis工作流程
- 加载配置——配置方式主要有两种,一种是XML文件配置,另一种是java代码注解,Mybatis将sql配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置,执行sql事务语句,结果隐射配置),将其存储发哦内存中。
- SQL解析——当API接口层接收到调用请求时,会接受传入sql的id和传入对象(可以是Map,javabean或基本数据类型),Mybatis会根据SQL的id查找到相应的MappedStatement,然后根据传入的参数对象对Mapped Statement对象进行解析,解析后便得到最终需要执行的sql语句和参数。
- SQL执行——将最终的sql和参数拿到数据库进行执行,得到需要的结果
- 结果映射——将操作得到的数据库的结果按照映射配置进行转换,可以将其转换成HashMap,javabean或者基本数据类型,并将最终结果返回。
二.Mybatis搭建
- 导入jar包
- 书写相信的bean和DAO接口
UserBean
public class User {
private int userId;
private String userName;
private String password;
private String info;
//省略get和set方法
}
DAO接口(操作数据库的方法)
public interface UserDAO {
public List<User> getAll();
//@Param映射文件使用的名字
public User getUserById(@Param("uId") int userId);
public int addUser(User user);
public List<User> getByName(String search);
}
- 资源配置文件(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">
<configuration>
<!-- 配置数据库环境 -->
<environments default="test"> <!-- 默认环境选择test -->
<!--两个允许环境一个属于测试环境,一个属于正式上线的环境-->
<environment id="test">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,不适用连接池 -->
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/javaweb_day7"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="main">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,不适用连接池 -->
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/javaday_7?characterEncoding=utf8"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 配置DAO映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
- 映射配置文件(UserMapper.xml)——用于定义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="cn.goktech.dao.UserDAO">
<!--
增删改查
id指定方法名
resultType:返回值类型,集合写直属子元素类型;
parameterType:指定参数类型,多参数可写类类型
-->
<select id="getAll" resultMap="userMap" >
select * from user
</select>
<select id="getUserById" resultMap="userMap" parameterType="int">
select * from user where userId=#{uId}
</select>
<!--
模糊查询,${}和#{}的区别:
#{}会添加引号,${}不会默认添加引号,
${}容易造成sql注入,${}一般用于传入数据库对象,比如表明,类名
s -->
<select id="getByName" resultType="cn.goktech.entity.User" parameterType="String">
select * from user where userName like '%${value}%' <!--或 like CONCAT('%',#{search},'%') -->
</select>
<insert id="addUser" parameterType="cn.goktech.entity.User">
insert into user(userName,password,info) values (#{userName},#{password},#{info})
</insert>
<!-- mysql字段与类属性的映射 -->
<resultMap type="cn.goktech.entity.User" id="userMap">
<!--
id映射使用id标签,其他使用result
property是类中的属性字段,
column是数据库对应的字段
-->
<id property="userId" column="userId"/>
<result property="userName" column="userName"/>
<result property="password" column="password"/>
<result property="info" column="info"/>
</resultMap>
<!-- <delete id=""></delete>
<update id=""></update> -->
</mapper>
每一个select、insert、update、delete的id与DAO接口的方法名对应
三.Mybatis的API
- SqlSessionFactoryBuilder——该对象负责根据配置文件mybatis-config.xml构建SqlSessionFactory实例
- SqlSessionFactory——每一个Mybatis的应用程序都将以一个SqlSessionFactory对象为核心,该对象负责构建SqlSession实例
- SqlSession——该对象包含了所有执行Sql的操作方法,用来执行已映射的sql语句,不过SqlSession打开数据库,真正执行交互的是Executor,SqlSession对数据库的操作都是通过Executor来完成的,Executor是动态创建的,不需要我们去管理。
//使用类加载器加载mybatis配置文件,并构建sqlSession工厂
InputStream is = Test.class.getClassLoader().getResourceAsStream("config/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = sqlSessionFactory.openSession();
//str映射sql的标识字符串,为要执行的方法的全类名,根据这个找到映射文件中对应的sql语句
String str = "cn.goktech.dao.UserDAO.getUserById";
//执行查询返回一个user对象的sql
User user = sqlSession.selectOne(str, 1001);
System.out.println(user.getUserName()+"---"+user.getInfo());
四.Mybatis实现CURD
- 查询
//使用类加载器加载mybatis配置文件,并构建sqlSession工厂
InputStream is = Test.class.getClassLoader().getResourceAsStream("config/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = sqlSessionFactory.openSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
List<User> uList = userDAO.getAll();
for (User u : uList) {
System.out.println(u.getUserName()+"---"+u.getInfo());
}
User user = userDAO.getUserById(1001);
System.out.println("user.getUserName="+user.getUserName());
- 增加
// 添加用户测试
User u1 = new User();
u1.setUserName("Clover");
u1.setPassword("123456");
u1.setInfo("文艺小青年");
int count = userDAO.addUser(u1);
sqlSession.commit();//提交事务
System.out.println("count="+count);
五.Mybatis的#和$取值
- 当传入参数为String类型时:
使用#{}方式可以通过#{参数名}或者默认的#{_paramenter}来获取参数值,但是使用 {参数名}来获取,需要通过默认方式${value}来取值
- #{}和${}取值的区别:
- #{}进行参数获取时,会把参数转成字符串,即会自动加上一个引号 ‘ ’
如 select * from bank where name= #{name} ,传入名字 “ zhangsan”
最终得到的是 :select * from bank where name= ‘ zhangsan’- ${}是将传入的数据直接显示生成在sql中:
select * from bank where name= ${value} 传入名字 “zhangsan”
最终得到: select * from bank where name = zhangsan ,
直接运行报错,所以需要我们手动添加 ‘ ‘ : select * from bank where name= ‘ ${value} ’
但是这样做会带来非常严重的sql注入
比如传入这样的参数:userDAO.getUserIByPsd(" pwd111 ’ OR ‘1’='1 ")
实际上是 select * from bank where password= ’ pwd111 ’ OR ‘1’=‘1’
- ${}使用场景
多数情况下我们应该使用#{ },但是有些时候我们需要使用${},比如进行动态排序时,根据传入字段排序:
SELECT * FROM bank order by #{ columnName},比如说按照工资排序,你传入salary这个字段,结果:SELECT * FROM bank order by ‘salary’,这样明显排序时不起作用的,我们需要 SELECT * FROM bank order by ${ columnName}。所以说 ${ }方式一般用于传入数据库对象,例如传入表名 ,列名这种。
- Mybatis多参数映射查询
当sql语句只需要一个参数作为条件时,那么只要通过#{参数名}便可获得该参数值,但是当有多个参数时,这样会出错,解决方法如下:
- 将多个参数传入map,然后#{keyName}取值
- 将多个参数封装到实体类,然后#{属性名}取值
- Mybatis还提供了一个使用注解来参入多个参数的方式,这种方式需要在接口的参数上添加**@Param**注解:
public UserBean checkLogin(@Param(value="name")String userName,@Param(value="password")String password); //在映射文件中便可以通过 #{name} #{password} 取出
- 模糊查询
模糊查询不能使用#取值的方式, like %#{name}% 不能实现模糊查询,使用$取值的方式,like ‘%${value}%‘可以实现,但是一般不适用此方式,一般使用 like concat(’%’,#{name},’%’)的方式进行模糊查询
- 特别的异常处理
在进行增删查改的时候如果在sql语句中有>或<z,则会报错:
The content of elements must consist of well-formed character data or markup
需要在外部添加<![CDATA[SQL语句]]>,如:<select id="getEqualName" resultType="Map"> <![CDATA[ SELECT sname,COUNT(*) num FROM student GROUP BY sname HAVING COUNT(sname) >1 ]]> </select>
六.Mybatis关系映射
- sql和include元素:sql元素可以定义一条sql的一部分,方便后面的sql引用它。如下:
<!-- sql与include元素 -->
<sql id="begin">select * from teacher</sql>
<select id="getAll">
<include refid="begin" />
where tid>1
</select>
- 一对一映射
(1). 在实体类添加相应对象
public class Teacher {
private int tid;
private String tname;
private String phone;
private int cid;
private Cs cs; // 测试一对一:一个班主任带一个班
//省略了get和set方法
}
(2).在*Mapper.xml中使用assoaction标签
<!-- Mybatis级联 -->
<!-- 一对一 -->
<!-- resultMap:type结果返回类型 id标签:column指定表中的主键,property指定类中的属性 result标签:普通字段对应 -->
<resultMap type="Cs" id="cs">
<id column="cid" property="cid" />
<result column="cname" property="cname" />
<result column="introduce" property="introduce" />
</resultMap>
<resultMap type="Teacher" id="teacherMap">
<id column="tid" property="tid" />
<result column="tname" property="tname" />
<result column="phone" property="phone" />
<result column="cid" property="cid" />
<!-- 一对一关系映射,property指定属性名,javaType指定属性类型 -->
<association property="cs" resultMap="cs"/>
</resultMap>
<select id="oneToOne" resultMap="teacherMap">
select * from teacher t,cs c where t.cid=c.cid
</select>
- 一对多映射(一个班级对应多个学生)
(1). 在实体类添加相应集合
public class Cs {
private int cid;
private String cname;
private String introduce;
private List<Student> students;
//省略get和set方法
}
(2).在*Mapper.xml中使用collection标签
<resultMap type="Student" id="student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<result column="password" property="password"/>
<result column="score" property="score"/>
<result column="cid" property="cid"/>
</resultMap>
<resultMap type="Cs" id="cs">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<result column="introduce" property="introduce"/>
<!-- 一对多 -->
<collection property="students" resultMap="student"/>
</resultMap>
<select id="oneToMany" resultMap="cs">
select * from cs c,student s where c.cid=s.cid
</select>
七.动态SQL语句
- if元素:如果是字符串默认为null,如果是数字,默认为0
<select id="findEmp" parameterType="map" resultType="Emp">
SELECT* FROM EMP WHERE dept no=#{deptNo}
<if test="salary!=null">
AND salary=#{salary}
</if>
</select>
- choose-when-otherwise只会在多个选项中执行一个
<select id="search" parameterType="Student" resultType="Student">
select * from student where sname like '%${sname}%'
<!-- choose只会进入一个分支 -->
<choose>
<when test="password!=null">
and password like '%${password}%'
</when>
<when test="score!=0">
and score>#{score}
</when>
<otherwise>
order by sid desc
</otherwise>
</choose>
</select>
- where元素:where 元素知道在至少有一个以上的if元素满足条件时才去插入“WHERE”子句。而且,如果最后得到的sql子句是以“AND”或“OR”开头的,where 元素也知道如何将他们去除。
<!-- 测试where标签,动态拼接条件 -->
<select id="testWhere" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="sname!=null">
sname like CONCAT('%',#{sname},'%')
</if>
<if test="password!=null">
and password like '%${password}%'
</if>
<if test="score!=0">
and score >#{score}
</if>
</where>
</select>
- set元素:Set元素主要是用在更新操作的时候,它的主要功能和where元素相似,主要是在元素所在位置输出一个set关键字,而且还可以去除内容结尾中无关的逗号。有个set元素我们就可以动态的更新那些修改了的字段
<update id="updateStudent" parameterType="Student">
update student
<set>
<if test="sname!=null">
sname=#{sname},
</if>
<if test="score!=0">
score=#{score},
</if>
<if test="password!=null">
password=#{password}
</if>
</set>
where sid=#{sid}
</update>
- trim元素:
- prefix:可以给Trim元素包裹内容之前加上指定前缀,
- suffix:可以给Trim元素包裹内容之后加上某些后缀,
- prefixOverrides:可以把Trim元素包含内容的首部某些内容过滤,
- suffixOverrides:可以把Trim元素包含内容的尾部的某些内容过滤。
<update id="updateStudent" parameterType="Student"> update student <trim prefix="set" suffix="where sid=#{sid}" suffixOverrides=","> <if test="sname!=null"> sname=#{sname}, </if> <if test="score!=0"> score=#{score}, </if> <if test="password!=null"> password=#{password}, </if> </trim> </update>
- foreach元素:Foreach元素实现了循环逻辑,可以实现对一个集合的迭代,通常是在构建 IN 条件语句的时候。Foreach元素一般实现对三种类型数据的遍历:List, 数组array, Map三种。
元素简介:
- item(必选):表示集合中每一个元素进行迭代时的别名。
- collection(必选):表示传入过来的参数的数据类型,如果传入参数为List,则其属性值为list;传入数组则属性值为array;如果传入参数为User对象,而这个User对象有属性 List xxlist,那么属性值为collection =“xxlist”。Map稍后讲。
- index:在 list 和数组中,index 是元素的序号;在 map 中,index 是元素的 key。
- open:表示该语句以什么开始
- close:表示该语句以什么结束
- separator:表示在每次进行迭代之间以什么符号作为分隔符
<!-- forEach元素 --> <select id="award" resultType="Student"> select * from student where sid in <!-- collection:1.List写list,2数组写array,3.map写param.values --> <foreach collection="list" item="id" open="(" close=")" separator=","> #{id} </foreach> </select>
八.Spring整合Mybatis
- 创建javaweb项目,将之前教程的jar包全部导入进来
- 创建相应的DAO和实体类
- 配置jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/javaweb
username=root
password=123456
maxIdle=1000
4.配置Spring-mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans 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:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
">
<!-- 扫描注解配置 -->
<context:component-scan base-package="cn.goktech.*" />
<!-- 扫描RequestMapping注解 -->
<mvc:annotation-driven/>
<!-- 1.引用本地配置为文件 -->
<util:properties id="jdbc" location="classpath:jdbc.properties" />
<!-- 2.配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="#{jdbc.driver}"/>
<property name="url" value="#{jdbc.url}"/>
<property name="username" value="#{jdbc.username}"/>
<property name="password" value="#{jdbc.password}"/>
<property name="maxActive" value="#{jdbc.maxActive}"/>
</bean>
<!-- 3.配置sqlSessionFactory,通过Spring 来获取sqlSessionFactory实例,让Spring根据该工出厂实例获取sqlSession -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置mapp.xml文件所在位置 -->
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"></property>
<!-- 给包的类注册别名 -->
<property name="typeAliasesPackage" value="cn.goktech.entity"></property>
</bean>
<!-- 4.方式一:配置sqlSessionTemplate操作类
: 有了sqlSessionFactory以后,我们就开始要获取SqlSession了,
当mybatis和spring 整合以后, SqlSession都交由SqlSessionTemplate 来管理了,
所以我们需要创建对应的Bean-
-->
<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"></constructor-arg>
</bean> -->
<!-- 4.方式二:MapperFactoryBean 一个dao需要配置一个MapperFactoryBean -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 指定sqlSessionFactory -->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
<!--指定dao接口,MapperFactoryBean自动生成对应接口的动态代理对象,
然后我们再将其依赖注入到service中直接使用。
-->
<property name="mapperInterface" value="cn.goktech.dao.UserDAO"></property>
</bean>
<!--
4.方式三:扫描包与包中的注解类
自动扫描包,将该包中的接口全部作为mapper
注解标记:annotationClass属性,只扫描带该注解的接口
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.goktech.dao"></property>
<!-- 只扫描带有特定注解的接口 -->
<!-- <property name="annotationClass" value="cn.goktech.anno.DiyAnnotation"></property> -->
</bean>
</beans>