MyBatis是一个数据库访问框架,对jdbc进行了封装。
MyBatis的全局配置文件是一个xml文件,该文件没有名称和路径要求,需要我们在使用的时候手动导入。configuration标签下有多个配置标签:
DOCTYPE中所引用的.dtd(Document Type Definition)为文档类型定义,能对当前.xml文件进行规定和约束。通过它.xml文件可以使用相应的标签。(不同类型的.xml文件会引用到不同的.dtd文件)
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
<typeAliases>
<package name="com.bear.sxt.pojo">package>
typeAliases>
<environments default="test1">
<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>
<mapper resource="com/bear/sxt/mapper/empMapper.xml">mapper>
<package name="com.bear.sxt.mapper">package>
mappers>
configuration>
使用MyBatis框架后,SQL语句并不是直接写在DAO层中的,而是写在“实体类名+Mapper.xml”文件中的,DAO层可以调用Mapper.xml中的SQL实现来进行数据库操作(通过namespace.id来调用)。
P.S.MyBatis默认不会自动提交事务,所以更新、插入、删除操作需要手动提交事务。
可以是pojo对象、基本数据类型或者是map对象等。(更新、插入和删除三个标签由于返回值固定为所影响的行数,所以这些标签不用定义ResultType参数)
调用SQL语句的时候有时需要传入的参数,有两种使用传入参数的格式:#{}和${}。
#{},使用了占位符的方式(其底层为prepareStatement)
${},使用了字符串拼接(其底层为Statement)
<mapper namespace="com.bear.sxt.empMapper">
<select id="selectAll" resultType="com.bear.sxt.pojo.Emp">
select * from emp
select>
<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>
编写完mapper中的sql语句后即可编写相关的业务逻辑。因为mapper中只有sql语句,而与数据库连接,关闭数据库连接等操作是没有的,所以这写操作我都写在了dao层中。
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();
}
}
}
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;
}
为了解决方法一的弊端,可通过接口绑定的方式实现多参数传递。
public interface TransferLogMapper {
//使用这个模式时,.xml文件中引用参数需要用:#{0} #{1}...或#{param1} #{param2}...
//List 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>
<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;
}
session、io等用完了需要关闭,避免内存泄露。由于这些资源都实现了AutoCloseable接口,所以可以编写如下一个统一的方法进行关闭。
public static void close(AutoCloseable... closeables) {
try {
for (AutoCloseable c : closeables) {
if (null != c) {
c.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
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() {
}
}