MyBatis框架常用优化方法
一、优化之Session工具类
1、单独使用 MyBatis时,可以封装一个用来获取MyBatis中Session的工具类
回顾之前使用Mybatis来操作数据库的步骤:
(1)启动mybatis框架( SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder)
(2)通过 SqlSessionFactoryBuilder来得到SqlSessionFactory ;
(3)通过SqlSessionFactory 来创建SqlSession;
(4)读source文件下的mybatis.xml,将mybatis.xml文件转化成流
(5)创建SqlSession对象
public static SqlSession session;
//启动mybatis框架,读配置文件,获取Session对象
public void getSession() throws IOException{
// (1)启动mybatis框架
// SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
// 读source文件下的mybatis.xml,将mybatis.xml文件转化成流
InputStream ins = Resources.getResourceAsStream("mybatis.xml");
// (2)创建SQLSessionFactory工厂对象
SqlSessionFactory ssf = sfb.build(ins);
// (3)创建SqlSession对象
session = ssf.openSession();
}
每次操作数据库都要重复上述步骤,因此,可以像封装jdbcUtil一样,将它封装成一个工具类!
2、MybatisUtil.java
package cn.java.utils;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
//1.SqlSessionFactoryBuilder:
// 作用:负责构建SqlSessionFactory对象,使用build()方法可创建多个SqlSessionFactory对象
// 生命周期:只存在于方法体内,用过就不需要了
// 一旦创建了SqlSessionFactory,就不再需要该类了,一般用于局部变量
//2.SqlSessionFactory:MyBatis应用的核心
//作用:创建SqlSession对象
//生命周期:与应用的生命周期相同,作用于Application
// 单例模式:factory的实例化的过程是一个比较耗费性能的过程
// 一旦被创建就应该在应用的运行期间一直存在,保证有且只有一个factory,不要重新创建另一个,常使用单例模式
//3.SqlSession:用于请求或方法的作用域,用完之后需要关闭,不要占用资源
//作用:包含了执行SQL语句的所有方法
//对应一次数据库会话,会话结束必须关闭
private static SqlSessionFactory factory;
//静态代码块,随着类的加载而执行,而且只执行一次
static{
try {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
factory = new SqlSessionFactoryBuilder().build(is);
// threadLocal = new ThreadLocal<SqlSession>();
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession(){
SqlSession session=null;
//通过session工厂获取到一个session
session = factory.openSession();
//调用session的查询集合方法
return session;
}
// 关闭SqlSession
public static void closeSession(SqlSession sqlSession){
sqlSession.close();
}
//测试
public static void main(String[] args) {
//System.out.println(MybatisUtil.factory==MybatisUtil.factory);
for (int i = 0; i < 5; i++) {
System.out.println(MybatisUtil.factory.hashCode());
}
}
}
以后每次使用只需要调用即可,不用重复编写加载配置文件和创建SqlSession代码,数据库操作完成后,SqlSession关闭。
二、优化之将连接字符串写入配置文件
引入数据库配置文件
<!-- 引入数据库配置文件 -->
<properties resource="database.properties"/>
<settings>
<setting name="logImpl" value="LOG4j"/>
</settings>
<!-- 配置数据库连接环境:driver、url、username、password -->
<environments default="mysql">
<!-- 开始配置mysql -->
<environment id="mysql">
<!--配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
三、优化之去DAO层
目的是简化项目结构
Mybatis局部配置文件名一定要与它所支持的mapper层中的接口同名,如:UserMapper.xml要与UserMapper.java同名。可以将mapper层接口与Mybatis局部配置文件放在同一个包下,mapper层接口中定义的方法名,一定要与Mybatis局部配置文件的SQL语句的id同名,注意mapper层中,没有接口的实现类。
四、优化之使用@Before、@After注解
1、@Before注解
在测试类中的某个方法前加@Before注解,可以使得在执行其他方法前,先执行该方法。
//Before注解:在每次调用测试方法之前,自动调用init()方法
@Before
public void init(){
session= MybatisUtil.getSession();
//um就是Mapper的实现类
um=session.getMapper(UserMapper.class);
}
2、@After注解
在测试类中的某个方法前加@After注解,可以使得在执行其他方法后,再执行该方法。
//每次调用测试方法之后,自动调用一下destory()
@After
public void destory(){
MybatisUtil.closeSession(session);
}
五、优化之使用@Select注解
六、优化之使用别名
给包下所有的实体类取别名
<!-- 配置别名 -->
<typeAliases>
<typeAlias type="cn.java.pojo.Student" alias="student"/>
<typeAlias type="cn.java.pojo.User" alias="user"/>
</typeAliases>
七、MyBatis 框架的优化练习
1、数据库准备
在MySchool数据库中,建立一个student表,表结构如下:
student(
id bigint(20) 自增,
sname varchar(40),
dept varchar(40),
age int)
并在表中插入几条数据,其中一条为自己的信息
2、完整代码
database.properties
#mysql8
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///myschool?serverTimezone=Hongkong
username=root
password=你的密码
log4j.properties
log4j.rootLogger=DEBUG, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
mybatis.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>
<!-- 引入数据库配置文件 -->
<properties resource="database.properties"/>
<!-- 开启log4j,输出SQL语句 -->
<settings>
<setting name="logImpl" value="LOG4j"/>
</settings>
<!-- 配置别名 -->
<typeAliases>
<typeAlias type="cn.java.pojo.Student" alias="student"/>
<typeAlias type="cn.java.pojo.User" alias="user"/>
</typeAliases>
<!-- 配置数据库连接环境:driver、url、username、password -->
<environments default="mysql">
<!-- 开始配置mysql -->
<environment id="mysql">
<!--配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 关联局部SQL映射配置文件 ,在每一个mapper里,指定SQL映射文件名及全路径,可使用“copy qualified name””-->
<mappers>
<package name="cn.java.mapper"/>
</mappers>
</configuration>
Student.java
package cn.java.pojo;
public class Student {
private Long id;
private String sname;
private String dept;
private int age;
public Student() {
}
public Student(Long id, String sname, String dept, int age) {
this.id = id;
this.sname = sname;
this.dept = dept;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student[" + "id=" + id + ", " +
"sname='" + sname + '\'' +
", dept='" + dept + '\'' +
", age=" + age +
']';
}
}
StudentMapper.java
package cn.java.mapper;
import cn.java.pojo.Student;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
// 该方法使用Student实体类
public List<Student> getAllStudent();
//直接在以下方法上加@Select注解,简化代码
@Select("select * from student")
public List<Student> findAllStudent();
// 该方法返回多条记录,不使用实体类,用Map数据类型去接受
public List<Map<String, Object>> getAllStudentMap();
// 该方法使用了带一个参数的查询语句,返回一条记录
public Map<String, Object> getStudentById(long id);
// 该方法使用了有多个参数的 select语句
public Map<String, Object> getStudentByMulCondition(Map<String, Object> map);
// 该方法插入一条记录,带参数,更新操作一定要提交事务
public int addStudent(Map<String, Object> map);
// 该方法插入多条记录,带参数,更新操作一定要提交事务
public int addStudentBatch(List<Student> list) ;
// 该方法使用了动态查询,查询条件不确定
public List<Map<String, Object>> getStudentByDynam(Map<String, Object> map) ;
// 该方法使用了动态修改,查询条件不确定
public int updateStudentByDynam(Map<String, Object> map) ;
// 根据id删除记录
public long deleteStudentById(long id);
// 根据多个id删除多条记录
public int deleteStudentByIds(long[] ids);
}
StudentMapper.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">
<!--该文件名必须与mapper文件中的接口名一致-->
<!-- sql语句保存在Mybatis的局部配置文件中
解释:
(1)namespace:命名空间,其值为某一个dao层接口的具体路径,
表示这个类要使用相应的SQL语句,这个具体路径不要自己写,可以选中该类,右键,选择“copy reference”,然后粘贴即可
(2)select标签存放查询语句;
(3)id:在整个配置文件中id值必须唯一,一般情况下,其值与dao层类中的使用该SQL语句的方法名保持一致;
(4)resultType:指定当前sql查询语句返回的数据类型。类型不是sql语句的最终类型,
而是某一条数据的类型,一般用实体类表示,也要用该实体类的“全路径”来表示,运行时系统会自动将实体类的对象创建出来
(5)可以编写多条sql语句
-->
<mapper namespace="cn.java.mapper.StudentMapper">
<!--
1.select语句返回多条User实体对象
resultType="cn.java.entity.User"表示返回User实体类
-->
<select id="getAllStudent" resultType="student">
SELECT * FROM student
</select>
<!--
2.select语句返回List<Map<String,Object>,可以不使用实体类,直接用Map数据类型,更加简单,简化程序
-->
<select id="getAllStudentMap" resultType="Map">
SELECT * FROM student
</select>
<!--
3.SQL语句带一个参数
parameterType:指定SQL语句接收的参数类型
-->
<select id="getStudentById" resultType="map" parameterType="Long">
SELECT * FROM student WHERE id=#{0}
</select>
<!--
4.SQL语句带多个参数,用Map封装,parameterType=Map,返回一条记录,按key取参数值
#在获取参数时可防止SQL注入攻击,应尽量使用#;模糊查询时,使用$
-->
<select id="getStudentByMulCondition" resultType="map" parameterType="Map">
SELECT * FROM student WHERE sname='${sname}' AND dept='${dept}' AND age='${age}'
</select>
<!-- 添加数据
5. delete、insert、update操作没有resultType属性,默认返回int型
parameterType=Map,表示参数类型为Map,用Map封装参数
#表示在获取参数时可防止SQL注入攻击,应尽量使用#;模糊查询时,使用$
INSERT INTO users SET username=,这种SQL语句是MmySql特有的扩展功能
-->
<insert id="addStudent" parameterType="Map">
INSERT INTO student SET sname=#{sname},dept=#{dept},age=#{age}
</insert>
<insert id="addStudentBatch" parameterType="student">
insert into
student(sname,dept,age)
values
<foreach collection="list" item="student" separator=",">
(#{student.sname},#{student.dept},#{student.age})
</foreach>
</insert>
<!--
6.动态SQL语句,实现动态查询
<:小于
>:大于
where标签:当有查询条件时,就使用where命令,当没有查询条件时,就不用where命令
if test:判断是否对某个字段进行查询
-->
<select id="getStudentByDynam" resultType="Map" parameterType="Map">
SELECT * FROM student
<where>
<if test="sname!=null">
sname=#{sname}
</if>
<if test="dept!=null">
and dept=#{dept}
</if>
<if test="age!=null">
and age=#{age}
</if>
</where>
</select>
<!--
7.动态修改update语句
set标签:当有更新字段时,就使用set命令,当没有更新字段时,
语句为UPDATE course SET id=NULL WHERE id=NULL
-->
<update id="updateStudentByDynam" parameterType="Map">
update student
<set>
<if test="sname!=null">
sname=#{sname},
</if>
<if test="dept!=null">
dept=#{dept},
</if>
<if test="age!=null">
age=#{age},
</if>
id=#{id}
</set>
where id=#{id}
</update>
<!--
8.根据id删除记录
根据id删除学生记录
-->
<delete id="deleteStudentById" parameterType="long">
DELETE FROM student WHERE id=#{id}
</delete>
<!--
9.根据多个id删除记录
根据多个id删除多个学生记录
-->
<delete id="deleteStudentByIds" parameterType="long[]" >
DELETE FROM student WHERE id IN
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>
MybatisUtil.java
package cn.java.utils;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
// 获取SqlSession
public static SqlSession getSession(){
SqlSession session=null;
InputStream inputStream=null;
try {
//配置文件的路径
String resource = "mybatis.xml";
//加载配置文件,得到一个输入流
inputStream = Resources.getResourceAsStream(resource);
//获取MyBatis的Session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//通过session工厂获取到一个session
session = sqlSessionFactory.openSession(true); //true表示自动提交事务
//调用session的查询集合方法
return session;
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
// 关闭SqlSession
public static void closeSession(SqlSession session){
if(session!=null){
session.close();
}
}
}
Test_Student.java
import cn.java.mapper.StudentMapper;
import cn.java.pojo.Student;
import cn.java.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test_Student {
// SqlSession session=null;
SqlSession session=null;
StudentMapper um=null;
//Before注解:在每次调用测试方法之前,自动调用init()方法
@Before
public void init(){
session= MybatisUtil.getSession();
//um就是Mapper的实现类
um=session.getMapper(StudentMapper.class);
}
//每次调用测试方法之后,自动调用一下destory()
@After
public void destory(){
MybatisUtil.closeSession(session);
}
@Test
public void testGetAllStudent(){
um= session.getMapper(StudentMapper.class);
List<Student> studentList = um.getAllStudent();
for (Student student : studentList) {
System.out.println(student);
}
}
@Test
public void testFindAllStudent(){
List<Student> studentList = um.findAllStudent();
for (Student student : studentList) {
System.out.println(student);
}
}
@Test
// 2.该方法返回多条记录,不使用实体类,用Map数据类型去接受
public void testGetAllStudentMap() {
List<Map<String, Object>> studentList =um.getAllStudentMap();
for (Map<String, Object> map : studentList) {
System.out.println(map);
}
}
@Test
// 3.该方法使用了带一个参数的查询语句,返回一条记录
public void testGetStudentById() {
// 传递参数,直接传
Long id =20201002222L;
Map<String, Object> studentMap = um.getStudentById(id);
System.out.println(studentMap);
}
@Test
// 4.该方法使用了有多个参数的 select语句
public void testGetStudentByMulCondition() {
// 声明一个Map对象,可以使用Map或实体类同时传递多个参数,用map更简单
Map<String, Object> paramMap = new HashMap<String, Object>();
// 封装参数
paramMap.put("sname", "李四");
paramMap.put("dept", "软件工程");
paramMap.put("age", "21");
// 传递参数
Map<String, Object> studentMap = um.getStudentByMulCondition(paramMap);
System.out.println(studentMap);
}
// 5.该方法插入一条记录,带参数,更新操作一定要提交事务
@Test
public void testAddStudent() {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("sname", "徐三");
paramMap.put("dept", "计算机");
paramMap.put("age", "19");
int resultInt = um.addStudent(paramMap);
System.out.println(resultInt);
}
// 6.该方法插入多条记录,带参数,更新操作一定要提交事务
@Test
public void testAddStudentBatch() {
List<Student> list = new ArrayList<>();
Student student;
for (int i = 0; i < 5; i++) {
student = new Student();
student.setSname("test" + i);
student.setDept("网络安全");
student.setAge(19);
list.add(student);
}
int resultInt = um.addStudentBatch(list);
System.out.println(resultInt);
};
// 7.该方法使用了动态查询,查询条件不确定
@Test
public void getStudentByDynam() {
// 可以使用Map或实体类同时传递多个参数
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("age", "20");
List<Map<String, Object>> studentList =um.getStudentByDynam(paramMap);
for (Map<String, Object> map : studentList) {
System.out.println(map);
}
}
// 8.该方法使用了动态修改,查询条件不确定
@Test
public void updateStudentByDynam() {
// 可以使用Map或实体类同时传递多个参数
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("id","20201001111");
paramMap.put("dept", "计算机");
int resultInt = um.updateStudentByDynam(paramMap);
System.out.println(resultInt);
}
@Test
public void deleteStudentById(){
long id = 20201005582L;
long resInt = um.deleteStudentById(id);
System.out.println(resInt);
}
@Test
public void deleteStudentByIds(){
long[] ids = new long[]{20201005574L,20201005575L,20201005576L,
20201005577L,20201005578L,20201005579L,20201005581L};
long resInt = um.deleteStudentByIds(ids);
System.out.println(resInt);
}
}