目录
1.概述
MyBatis是一个数据库访问框架,对jdbc进行了封装。
2.环境搭建与使用
2.1.导入相关依赖
2.2.新建全局配置文件
MyBatis的全局配置文件是一个xml文件,该文件没有名称和路径要求,需要我们在使用的时候手动导入。configuration标签下有多个配置标签:
- settings:设置,可以用于配置所使用的日志输出依赖
- typeAliases:别名,可以对类和包起别名,方便使用
- environment:环境,这是myBatis全局配置文件中最重要的一个标签,其定义了与数据库连接的相关信息,如数据库驱动、数据库的url,用户名,密码等。
- mapper:包含了SQL命令的xml配置文件的路径。
DOCTYPE中所引用的.dtd(Document Type Definition)为文档类型定义,能对当前.xml文件进行规定和约束。通过它.xml文件可以使用相应的标签。(不同类型的.xml文件会引用到不同的.dtd文件)
<?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>
<settings>
<!--配置使用的日志输出依赖-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<!--给某个类起别名,可以在Mapper.xml的select标签的参数类型和返回类型中使用该别名-->
<!--<typeAlias type="com.bear.sxt.pojo.Emp" alias="emp"></typeAlias>-->
<!--给某个包里面的所有类起别名,使用这些类时可以省略包名直接用类名-->
<package name="com.bear.sxt.pojo"></package>
</typeAliases>
<environments default="test1">
<!--environment中包含了连接数据库的基本信息-->
<environment id="test1">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user_infos?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--这里包含了定义了数据库操作的.xml配置文件-->
<mapper resource="com/bear/sxt/mapper/empMapper.xml"></mapper>
<!--使用getMapper形式的话需要写接口与mapper.xml文件所在的包-->
<package name="com.bear.sxt.mapper"></package>
</mappers>
</configuration>
2.3.编写Mapper.xml文件
使用MyBatis框架后,SQL语句并不是直接写在DAO层中的,而是写在“实体类名+Mapper.xml”文件中的,DAO层可以调用Mapper.xml中的SQL实现来进行数据库操作(通过namespace.id来调用)。
2.3.1.标签
- select:查询
- update:更新
- insert:插入
- delete:删除
P.S.MyBatis默认不会自动提交事务,所以更新、插入、删除操作需要手动提交事务。
2.3.2.标签中的属性
ResultType:返回值类型
可以是pojo对象、基本数据类型或者是map对象等。(更新、插入和删除三个标签由于返回值固定为所影响的行数,所以这些标签不用定义ResultType参数)
parameterType:参数类型
调用SQL语句的时候有时需要传入的参数,有两种使用传入参数的格式:#{}和${}。
-
#{},使用了占位符的方式(其底层为prepareStatement)
- #{属性名}:如传入的是一个pojo对象,则会调用成员变量的get方法来获取值。
- #{key}:如果传入的是一个map对象,则会通过key值获取相应的值。
- #{index}:如果传入的是一个数组(或使用接口绑定的形式传入一系列的采参数),则可以通过索引值来表示调用第几个数。(index从0开始起算)
- #{param1}:与#{index}类似,但采用param1、param2…来当作索引值。
- #{whatever}:如果传入的是一个基本数据类型或者是一个String对象,则{}内写什么都行。
-
${},使用了字符串拼接(其底层为Statement)
- ${属性名}:同#{属性名}。
- ${number}:若{}中为一个数字,则值为该数字本身。
<?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:实现类的全路径(调用SQL语句时,会使用com.bear.sxt.log.selectId进行调用)-->
<mapper namespace="com.bear.sxt.empMapper">
<!--
id:方法名
parameterType:参数类型
resultType:返回值类型(通过auto mapping的方式,匹配实体类中的set方法(字段名需要和实体类对应的属性名一致)或直接给属性赋值)
-->
<!--查询-->
<select id="selectAll" resultType="com.bear.sxt.pojo.Emp">
select * from emp
</select>
<!--插入,#{salary}会调用具体的get方法获取传入对象的成员变量-->
<insert id="insertEmp" parameterType="com.bear.sxt.pojo.Emp">
insert into emp (empname, salary, birthday, deptId, age)
values (#{empname}, #{salary}, #{birthday}, #{deptId}, #{age});
</insert>
<!--更新-->
<update id="updateEmpSalary" parameterType="map" >
update emp set salary=#{salary} where id=#{id}
</update>
<!--删除-->
<delete id="deleteEmp" parameterType="int">
delete from emp where id=#{id}
</delete>
</mapper>
2.4.编写相关业务逻辑
编写完mapper中的sql语句后即可编写相关的业务逻辑。因为mapper中只有sql语句,而与数据库连接,关闭数据库连接等操作是没有的,所以这写操作我都写在了dao层中。
2.4.1.编写工具类
public class MyBatisUtil {
/*factory在实例化过程中是一个比较耗时的过程,所以在程序中保证只有一个factory*/
private static SqlSessionFactory factory = null;
/*采用threadLocal存储session-由于servlet->service->dao都是同一个线程的,所以采用ThreadLocal可以获取同一个session*/
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
static {
InputStream is = null;
try {
URL url = getClassLoader().getResource("myBatis.xml");
if (null != url) {
is = new FileInputStream(new File(url.getPath()));
factory = new SqlSessionFactoryBuilder().build(is);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
close(is);
}
}
/**
* 获取session
* @return
*/
public static SqlSession getSession() {
SqlSession session = tl.get();
if (null == session) {
tl.set(factory.openSession());
}
return tl.get();
}
/**
* 关闭session
*/
public static void closeSession() {
close(tl.get());
tl.set(null);
}
/**
* 关闭与数据库相关的资源
* @param closeables 资源列表
*/
public static void close(AutoCloseable... closeables) {
try {
for (AutoCloseable c : closeables) {
if (null != c) {
c.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4.2.调用Mapper中的sql语句
方法1
- 通过“namespace.id”的方式调用Mapper中的sql语句。格式一般为:session.[select][delete][update][insert](“namespace.id”, parameter)。
- 这种方式由于只能接收基本数据类型、pojo、map三种形式的参数,所以传递多个参数时要不用pojo,要不构建一个map放入多个参数。而且每次都要写“namespace.id”字符串,比较容易出错。
public List<Emp> selectPage(int pageNum, int pageSize) {
SqlSession session = MyBatisUtil.getSession();
List<Emp> list = new ArrayList<>();
if (null != session) {
//构建参数map
Map<String, Integer> map = new HashMap<>();
map.put("pageStart", (pageNum - 1) * pageSize);
map.put("pageSize", pageSize);
//使用namespace.id调用mapper.xml中的sql语句
list = session.selectList("com.bear.sxt.empMapper.selectPage", map);
MyBatisUtil.closeSession();
}
return list;
}
方法2
为了解决方法一的弊端,可通过接口绑定的方式实现多参数传递。
- 优点为:
- 可使用“接口名.方法名”的方式来待用Mapper.xml中的sql语句。
- 之前.xml文件中的标签只能定义一个参数(基本数据类型、pojo、map),当有多参数时需要构建pojo、map。现在可以直接在接口中定义多参数的方法,框架会自动构建map对象映射到.xml的sql语句中。所以.xml的标签中可以不写parameterType属性了。
- 操作为:
- 在Mapper.xml所在的包中定义一个同名的接口,定义相应的抽象方法。
public interface TransferLogMapper {
//使用这个模式时,.xml文件中引用参数需要用:#{0} #{1}...或#{param1} #{param2}...
//List<TransferLog> selectPage(int pageStart, int pageSize);
//使用这种模式时,.xml文件中引用参数需要用:#{自定义的参数名}
//@Param("pageStart")中的字符串相当于key,形参相当于value
List<TransferLog> selectPage(@Param("pageStart") int pageStart, @Param("pageSize") int pageSize);
int count();
int insertLog(TransferLog log);
}
2.在全局配置文件的mappers标签中使用package标签指明Mapper.xml与接口所在的包
<mappers>
<!--使用getMapper形式的话需要写接口与mapper.xml文件所在的包-->
<package name="com.bear.sxt.mapper"></package>
</mappers>
3.编写Mapper.xml文件
1)mapper标签中的namespace必须为同名接口的全限定路径(包名.类名)。
2)select、update、delete和insert标签中的id必须与同名接口中所定义的相应方法一一对应。
4.使用
使用session.getMapper(interface.class)方法获取接口的实现对象,然后调用其中的方法。
public int insertLog(TransferLog log) {
SqlSession session = MyBatisUtil.getSession();
int result = 0;
if (null != session) {
TransferLogMapper tlm = session.getMapper(TransferLogMapper.class);
result = tlm.insertLog(log);
session.commit();
MyBatisUtil.closeSession();
}
return result;
}
2.4.3.关闭资源
session、io等用完了需要关闭,避免内存泄露。由于这些资源都实现了AutoCloseable接口,所以可以编写如下一个统一的方法进行关闭。
public static void close(AutoCloseable... closeables) {
try {
for (AutoCloseable c : closeables) {
if (null != c) {
c.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
3.统一session的获取与关闭
- 在上述代码中每次用完session都需要调用MyBatisUtil.closeSession()来进行关闭,为了简化该操作,可以在过滤器中统一session的关闭与获取。
- 采用拦截器对所需请求进行拦截,拦截后自动获取session,完成了servlet中的逻辑后,再关闭session。由于MyBatisUtil中使用了ThreadLocal,而同一组的servlet、service和dao都是处于同一线程的,所以能获取到的session是一样的。
package com.bear.sxt.filter;
import com.bear.sxt.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class OpenSessionInView implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
SqlSession session = MyBatisUtil.getSession();
try {
//运行servlet中的内容
filterChain.doFilter(servletRequest, servletResponse);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}finally {
MyBatisUtil.closeSession();
}
}
@Override
public void destroy() {
}
}