本文涉及SSM框架中MyBatis知识点,详细见目录。
MyBatis是一个半自动的ORM框架,你给sql,我来执行。
ORM(Object Relation Mapping)对象关系映射,将Java中的一个对象与数据表中的一行记录一一对应。
ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,将文件保存到了数据表中,实现了对象的持久化。
MyBatis特点:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
说明,在pom.xml中添加包的说明以后,需要到Maven中刷新一下,将包安装进来
需要先定义MyBatis配置模版,其他的模版也可以在这里进行配置
选择resources--右键New--Edit File Templates
<?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>
</configuration>
在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配置数据库连接信息 -->
<!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!-- default属性,用来执行使用哪个environment 标签,-->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager用于配置数据库管理方法 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource标签用来配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
案例:学生信息的数据库操作
CREATE TABLE tb_students(
sid int PRIMARY key auto_increment,
stu_num CHAR(5) not null UNIQUE,
stu_name VARCHAR(20) not null,
sut_gender CHAR(2) not null,
stu_age int not null
);
导入lombok,然后添加注释
package com.MyBatisDemo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int stuId;
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
}
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(Student stuNum);
}
模版:
<?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>
在resources目录下,新建mappers文件夹
在mappers中新建StudentMapper.xml文件,这个可以根据上面的模版进行创建,注意这里的StudentMapper名称建议和DAO联系起来
在映射文件中,对DAO中的方法进行实现
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.MyBatisDemo.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
insert into tb_student(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
<delete id="deleteStudent">
delete from tb_student where stu_num = #{stuNum}
</delete>
</mapper>
<?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配置数据库连接信息 -->
<!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!-- default属性,用来执行使用哪个environment 标签,-->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager用于配置数据库管理方法 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource标签用来配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
</configuration>
测试类往往是在测试类名后加Test,测试方法往往是在测试方法名前加Test
junit
在被测试接口类名后面, alt+insert --> test
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
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.Test;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.*;
public class StudentDAOTest {
@Test
public void insertStudent() {
try {
//加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//创建builder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//会话工厂,连接工厂
SqlSessionFactory factory = builder.build(is);
//sqlsession 代表数据库的连接,也代表数据库的连接对象
//会话(连接)
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// System.out.println(studentDAO);
int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
//需要手动提交
sqlSession.commit();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
@org.junit.Test
public void deleteStudent() {
}
}
案例:学生信息的增删改查
略
根据学号删除一条学生信息
步骤:
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
}
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.MyBatisDemo.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
<delete id="deleteStudent">
delete from tb_students where stu_num = #{stuNum}
</delete>
</mapper>
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
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.Test;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.*;
public class StudentDAOTest {
@Test
public void insertStudent() {
try {
//加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//创建builder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//会话工厂,连接工厂
SqlSessionFactory factory = builder.build(is);
//sqlsession 代表数据库的连接,也代表数据库的连接对象
//会话(连接)
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// System.out.println(studentDAO);
int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
//需要手动提交
sqlSession.commit();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testDeleteStudent() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//SqlSessionFactory表示mybatis的会话工厂,工厂模式
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过SqlSession 对象调用getMapper方法获取DAO接口对象
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//调用被测试方法
int i = studentDAO.deleteStudent("10001");
//提交事务
sqlSession.commit();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
}
根据学号,修改其他字段信息
@Test
public void testUpdateStudent(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
int i = studentDAO.updateStudent(new Student(3,"10002",".net小小少年","男",25));
sqlSession.commit();
assertEquals(1,i);//期望,单元测试的期望返回结果
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
import java.util.List;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
public int updateStudent(Student student);
public List<Student> listStudents();
}
<!-- resultType执行查询结果,封装的对象的实体类-->
<!-- resultSets指定当前操作返回的集合类型(可省略)-->
<!-- resultType返回的类型-->
<!-- <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
<!-- select-->
<!-- sid as stuId,-->
<!-- stu_num as stuNum,-->
<!-- stu_name as stuName,-->
<!-- stu_gender as stuGender,-->
<!-- stu_age as stuAge-->
<!-- from tb_students-->
<!-- </select>-->
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
<!--resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
</select>
@Test
public void testStudentList() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
List<Student> studentList = studentDAO.listStudents();
assertNotNull(studentList);
sqlSession.commit();
for (Student student : studentList
) {
System.out.println(student);
}
} catch (IOException e) {
}
}
根据学号查询一个学生信息
在StudentDAO中定义接口方法
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
import java.util.List;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
public int updateStudent(Student student);
public List<Student> listStudents();
public Student queryStudent(String stuNum);
}
在StudentDAOMapper.xml中配置StudentDAO接口的方法实现--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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.MyBatisDemo.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>
<delete id="deleteStudent">
delete
from tb_students
where stu_num = #{stuNum}
</delete>
<update id="updateStudent">
update tb_students
set stu_name = #{stuName},
stu_gender = #{stuGender},
stu_age = #{stuAge}
where stu_num = #{stuNum}
</update>
<!-- resultType执行查询结果,封装的对象的实体类-->
<!-- resultSets指定当前操作返回的集合类型(可省略)-->
<!-- resultType返回的类型-->
<!-- <select id="listStudents" resultType="com.MyBatisDemo.pojo.Student" resultSets="java.util.List">-->
<!-- select-->
<!-- sid as stuId,-->
<!-- stu_num as stuNum,-->
<!-- stu_name as stuName,-->
<!-- stu_gender as stuGender,-->
<!-- stu_age as stuAge-->
<!-- from tb_students-->
<!-- </select>-->
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
<!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
</select>
<select id="queryStudent" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
where stu_num = #{stuNum}
</select>
</mapper>
单元测试--查询不需要提交事物
@Test
public void testStudent() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
Student student = studentDAO.queryStudent("10002");
assertNotNull(student);
//查询不用提交事务
System.out.println(student);
} catch (IOException e) {
}
}
分页查询
在StudentDAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;
public interface StudentDAO {
/*
* 在MyBatis进行操作:
* 1:如果操作方法只有一个简单类型或者字符串类型的参数,
* 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
* 2:如果操作方法有一个对象类型的参数,
* 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
* 3:如果操作方法有一个Map类型的参数,
* 在Mapper配置中可以直接通过#{key}获取key的指定value值
* 4:如果操作方法有多个参数,该如何处理呢?
* 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
* 也可以通过@Param("key") 给参数添加注解的方式
*/
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
public int updateStudent(Student student);
public List<Student> listStudents();
public Student queryStudent(String stuNum);
// public List<Student> listStudentsByPage(HashMap<String,Integer> map);
// public List<Student> listStudentsByPage(int start,int pageSize);
public List<Student> listStudentsByPage(@Param("start") int start
,@Param("pageSize") int pageSize);
}
在StudentMapper.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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.MyBatisDemo.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>
<delete id="deleteStudent">
delete
from tb_students
where stu_num = #{stuNum}
</delete>
<update id="updateStudent">
update tb_students
set stu_name = #{stuName},
stu_gender = #{stuGender},
stu_age = #{stuAge}
where stu_num = #{stuNum}
</update>
<!-- resultType执行查询结果,封装的对象的实体类-->
<!-- resultSets指定当前操作返回的集合类型(可省略)-->
<!-- resultType返回的类型-->
<!-- <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
<!-- select-->
<!-- sid as stuId,-->
<!-- stu_num as stuNum,-->
<!-- stu_name as stuName,-->
<!-- stu_gender as stuGender,-->
<!-- stu_age as stuAge-->
<!-- from tb_students-->
<!-- </select>-->
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
<!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
</select>
<select id="queryStudent" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
where stu_num = #{stuNum}
</select>
<!-- <select id="listStudentsByPage" resultMap="studentMap">-->
<!-- select sid,-->
<!-- stu_num,-->
<!-- stu_name,-->
<!-- stu_gender,-->
<!-- stu_age-->
<!-- from tb_students limit #{start}, #{pageSize}-->
<!-- </select>-->
<select id="listStudentsByPage" resultMap="studentMap">
select sid,
stu_num,
stu_name,
stu_gender,
stu_age
from tb_students
limit #{start}, #{pageSize}
<!-- limit #{arg0}, #{arg1}-->
<!-- limit #{param1}, #{param2}-->
</select>
</mapper>
注意:如果DAO操作方法,没有通过@Param指定参数别名,在SQL中也可以通过MyBatis自带的arg0,arg1...或者param1,param2...获取参数
单元测试:
@Test
public void testlistStudentsByPage() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// HashMap<String,Integer> map = new HashMap<String,Integer>();
// map.put("start",0);
// map.put("pageSize",2);
// List<Student> studentList = studentDAO.listStudentsByPage(map);
List<Student> studentList = studentDAO.listStudentsByPage(0,2);
assertNotNull(studentList);
sqlSession.commit();
for (Student student : studentList
) {
System.out.println(student);
}
} catch (IOException e) {
}
}
StudentDAO
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;
public interface StudentDAO {
/*
* 在MyBatis进行操作:
* 1:如果操作方法只有一个简单类型或者字符串类型的参数,
* 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
* 2:如果操作方法有一个对象类型的参数,
* 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
* 3:如果操作方法有一个Map类型的参数,
* 在Mapper配置中可以直接通过#{key}获取key的指定value值
* 4:如果操作方法有多个参数,该如何处理呢?
* 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
* 也可以通过@Param("key") 给参数添加注解的方式
*/
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
public int updateStudent(Student student);
public List<Student> listStudents();
public Student queryStudent(String stuNum);
// public List<Student> listStudentsByPage(HashMap<String,Integer> map);
// public List<Student> listStudentsByPage(int start,int pageSize);
public List<Student> listStudentsByPage(@Param("start") int start
,@Param("pageSize") int pageSize);
public int getCount();
}
在StudentMapper.xml配置sql时,使用resultType指定当前操作的返回类型为int
<select id="getCount" resultType="int">
select count(1) from tb_students
</select>
单元测试:
@Test
public void testGetStudentCount(){
try{
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
int i = studentDAO.getCount();
System.out.println(i);
}catch (IOException e){
}
}
在StudentMapper.xml的insert标签中,useGeneratedKeys设置添加操作是否需要回填生成的主键keyProperty设置回填的位置
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<!--useGeneratedKeys 设置添加操作是否需要回填生成的主键-->
<!--keyProperty 设置回填的位置-->
<insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>
MyBatisUtil
package com.MyBatisDemo.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;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
public static SqlSession getSqlSession() throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession();
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
增删改,需要commit操作
sqlSession对象:
sqlSession.commit() 提交事务
sqlSession.rollback() 事务回滚
@Test
public void insertStudent() {
try {
// //加载mybatis配置文件
// InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// //创建builder
// SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// //会话工厂,连接工厂
// SqlSessionFactory factory = builder.build(is);
// //sqlsession 代表数据库的连接,也代表数据库的连接对象
// //会话(连接)
// SqlSession sqlSession = factory.openSession();
// StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// System.out.println(studentDAO);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//1:当我们获取sqlSession对象时,就默认开启了事务
try{
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
Student student = new Student(0, "10005", "java少年", "男", 24);
int i = studentDAO.insertStudent(student);
//2:操作完成并成功以后,需要手动提交
sqlSession.commit();
System.out.println(student);
System.out.println(i);
}catch (Exception e){
//3:当操作出现异常,调用rollback进行回滚
sqlSession.rollback();
}
} catch (IOException e) {
e.printStackTrace();
}
}
sqlSession = factory.openSession(true);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
MyBatisUtil优化
package com.MyBatis.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;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
测试操作:
@Test
public void testDeleteStudent() {
try {
// InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// //SqlSessionFactory表示mybatis的会话工厂,工厂模式
// SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
// //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
// SqlSession sqlSession = sqlSessionFactory.openSession();
// //通过SqlSession 对象调用getMapper方法获取DAO接口对象
// StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// //调用被测试方法
// int i = studentDAO.deleteStudent("10001");
// //提交事务
// sqlSession.commit();
// System.out.println(i);
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
int i = studentDAO.deleteStudent("10001");
} catch (IOException e) {
e.printStackTrace();
}
}
当有多个操作一般都使用手动提交
MyBatisUtil事务管理优化:
package com.MyBatisDemo.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;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
//手动事务管理
public static SqlSession getSqlSession() throws IOException {
return getSqlSession(false);
}
//自动事务管理
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
mybatis-config.xml是MyBatis框架的主配置文件,主要用于配置MyBatis数据源以及工作属性信息
注意:
在resource文件夹下创建jdbc.properties文件,配置键值对如下:
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8
mysql_username=root
mysql_password=123456
在mybatis-config.xml中通过properties标签引用jdbc.properties文件,引入后在配置environment时,可以直接使用jdbc.properties的key获取value
<!--设置mybatis的属性-->
<settings>
<!--启动二级缓存-->
<setting name="cacheEnable" value="true"/>
<!--启用延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<!--typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名-->
<typeAliases>
<typeAlias type="com.liguoqing.pojo.Student" alias="Student"></typeAlias>
</typeAliases>
<!--plugins主要用于配置MyBatis插件,例如分页插件-->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- environments配置数据库连接信息 -->
<!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!-- default属性,用来执行使用哪个environment 标签,-->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager用于配置数据库管理方法 type="JDBC" 可以进行事务的提交和回滚操作,type="MANAGED" 事务的提交和回滚由容器进行控制 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED 是否使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="${mysql_driver}"/>
<property name="url" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
</environments>
<!-- mappers用于载入映射文件,载入DAO类-->
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
可以参考源码流程
mapper文件相当与DAO接口的实现类,namespace属性要执行实现DAO的全限定名(Copy Reference)
声明添加操作(sql:insert...)
常用属性:
主键回填:
方式一:
<insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>
方式二:
<insert id="insertStudent">
<selectKey keyProperty="stuId" resultType="java.lang.Integer">
select last_insert_id()
</selectKey>
insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>
声明删除操作
声明修改操作
声明查询操作
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
设置当前dao进行数据库操作时的缓存属性设置
<cache type="" size="" readOnly="false"></cache>
sql片段
<sql id="sqlpianduan">sid,
stu_num,
stu_name,
stu_gender,
stu_age</sql>
<!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
select <include refid="sqlpianduan"/>
from tb_students
</select>
分页插件是一个独立于MyBatis之外的第三方插件;
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
在MyBatis的主配置文件中mybatis-config.xml 中通过 plugins标签进行配置,注意标签的位置
<!-- plugins主要用于配置MyBatis插件,例如分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
对学生信息进行分页查询
@Test
public void testGetStudentByPage() throws IOException {
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession
PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据
List<Student> studentList = studentDAO.listStudents();//查全量就可以,分页组件会自动将数据进行分页
PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
//返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据信信息
}
带条件的分页
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;
public interface StudentDAO {
public List<Student> listStudentsByGender(String gender);
}
————————————————————————————————————————————————————————————————————————————————
<select id="listStudentsByGender" resultMap="studentMap">
select <include refid="sqlpianduan"/>
from tb_students where stu_gender = #{stuGender}
</select>
—————————————————————————————————————————————————————————————————————————————————
@Test
public void testGetStudentByPageGender() throws IOException {
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
PageHelper.startPage(1,4);
List<Student> girls = studentDAO.listStudentsByGender("女");
PageInfo<Student> pageInfo = new PageInfo<Student>(girls);
//pageInfo中就包含了分页以及数据信息
for(Student s : pageInfo.getList()){
System.out.println(s);
}
}
实体--数据实体,实体关系指的就是数据和数据之间的关系
例如:用户和角色,房屋和漏洞,订单和商品
实体关系分为以下四种:
实例:人和身份证,学生和学生证,用户基本信息和详情
数据表关系:
实例:
数据表关系:
实例:用户和角色,角色和权限,房屋和业主,学生和社团,订单和商品
数据表关系:建立第三张关系表添加两个外键,分别与两张表的主键进行关联
用户--用户角色表--角色
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
</web-app>
添加依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
添加依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
帮助类
package com.MyBatisDemo.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;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
//手动事务管理
public static SqlSession getSqlSession() throws IOException {
return getSqlSession(false);
}
//自动事务管理
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
//加载主配置文件
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
实例:用户和详情
#用户信息表
create table users(
user_id int primary key auto_increment,
user_name varchar(20) not null unique,
user_pwd varchar(20) not null,
user_realname varchar(20) not null,
user_img varchar(100) not null
);
#用户详情表
create table details(
detail_id int primary key auto_increment,
user_addr varchar(50) not null,
user_tel char(11) not null,
user_desc varchar(200),
uid int not null unique
);
<!--这里的@注解是通过lombook包帮我们实现的-->
<!--这里重写ToString 是为了打印-->
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealName;
private String userImg;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Detail {
private int detailId;
private String userAddr;
private String userTel;
private String userDesc;
private int userId;
}
package com.MyBatisDemo.dao;
import com.MyBatisDemo.pojo.User;
public interface UserDao {
public int insertUser(User user);
}
测试代码:
@Test
public void testInsertUser() throws IOException {
//用户注册提交了基本信息和详情到Servlet,Servlet接收到注册信息,分装到User和Detail对象中
User user = new User(0,"zhangsan6","123123","张三","01.jpg",null);
Detail detail = new Detail(0,"安徽合肥市","15866666666","heihei",0);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try{
UserDao userDao = sqlSession.getMapper(UserDao.class);
int i = userDao.insertUser(user);//添加用户
System.out.println(i);
detail.setUserId(user.getUserId());
DetailDao detailDao = sqlSession.getMapper(DetailDao.class);
int n = detailDao.insertDetail(detail);//添加详情
System.out.println(n);
sqlSession.commit();//提交事务
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();//回滚事务
}
}
在查询用户的同时关联查询出与之对应的详情
实体
映射文件
子查询
测试代码:
@Test
public void testQueryUser() throws IOException {
UserDao userDao = MyBatisUtil.getMapper(UserDao.class);
User user = userDao.queryUser("zhangsan3");
System.out.println(user);
}
案例:班级(一)--学生(多)
#创建班级信息表
CREATE TABLE classes(
cid int PRIMARY key auto_increment,
cname VARCHAR(30) not null unique,
cdesc VARCHAR(100)
);
#创建学生信息表
CREATE TABLE students(
sid CHAR(5) PRIMARY key,
sname VARCHAR(20) not null,
sage int not null,
scid int not null
);
当查询一个班级的时候,要关联查询出这个班级下的所有学生
连接查询的映射配置
子查询
注意mapper与主配置文件的关系以及变脸重命名之间的配置
子查询的映射配置
实例:学生(n)-- 班级(1)
当查询一个学生的时候,关联查询这个学生所在的班级信息
连接查询的映射配置
子查询的映射配置
案例:学生(m)--课程(n)
#学生信息表如上
#课程信息表
CREATE TABLE courses(
course_id int PRIMARY key auto_increment,
course_name VARCHAR(50) not null
);
#选课信息表/成绩表(学号,课程号,成绩)
CREATE TABLE grades(
sid CHAR(5) not null,
cid int not null,
score int not null
);
查询学生时,同时查询出学生选择的课程
根据课程编号查询课程时,同时查询选择了这门课程的学生
连接查询的映射配置
子查询的映射配置
交友网,电商等网站都有筛选功能;
不同的需求筛选条件不同,需要动态的拼接sql;
用户的筛选条件不同,我们完成筛选执行的sql也不一样,我们可以通过穷举来完成不同条件的筛选,但是 这种实现思想过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件的查询。
根据搜索条件,动态完成SQL的拼接。
案例:心仪对象搜索
#数据表
CREATE TABLE members(
member_id int PRIMARY key auto_increment,
member_nick VARCHAR(20) not null UNIQUE,
member_gender char(2) not null,
member_age int not null,
member_city varchar(30) not null
);
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {
private int memberId;
private String memberNick;
private String memberGender;
private int memberAge;
private String memberCity;
}
在DAO接口中定义一个多条件查询的方法
public interface MemberDAO {
//在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
public List<Member> searchMember(HashMap<String,Object> params);
}
MemberMapper.xml
<resultMap id="memberMap" type="Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMember" resultMap="memberMap">
select member_id,member_nick,member_gender,member_age,member_city
from members
where 1 = 1
<if test="gender != null"><!--gender 就是参数对象的属性、参数Map的key-->
and member_gender = #{gender}
</if>
<if test="minAge != null"><!--> 大于号 -->
and member_age >= #{minAge}
</if>
<if test="maxAge != null"><!--< 小于号 -->
and member_age <= #{maxAge}
</if>
<if test="city != null">
and member_city != #{city}
</if>
</select>
MemberDAOTest
@Test
public void TestSearchMember() throws IOException {
HashMap<String,Object> params = new HashMap<String,Object>();
params.put("gender","女");
// params.put("minAge",19);
// params.put("maxAge",23);
// params.put("city","武汉");
//当向Map中存放参数时,key必须与动态Sql中的参数保持一致
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMember(params);
System.out.println(members);
}
测试类:
@Test
public void testSearchMemberByCity() throws IOException {
List<String> cities = new ArrayList<String>();
cities.add("武汉");
cities.add("厦门");
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByCity(cities);
for (Member m : members){
System.out.println(m);
}
}
案例:根据昵称查询会员信息(模糊匹配like)
public interface MemberDAO {
//根据昵称查询用户信息-- 模糊查询
//模糊查询需要使用 ${} 取值,与SQL进行拼接
//在使用${}时,即使只有一个参数也需要使用@Param注释声明参数的key (非String类型不需要)
public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
}
<!--如果参数是String 类型,需要使用parameterType声明参数类型-->
<select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
select member_id,member_nick,member_gender,member_age,member_city
from members
where member_nick like '%${keyWord}%'
</select>
@Test
public void testSearchMemberByNick() throws IOException {
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByNick("丽");
for (Member m : members){
System.out.println(m);
}
}
<!--${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句 可能引起SQL注入问题-->
<!--#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题-->
MyBatis作为一个封装好的ORM框架,其运行过程中我们没办法跟踪,为了让开发者了解MyBatis执行流程以及每个执行步骤所完成的工作,
MyBatis框架本身集成了log4j日志框架,对运行的过程进行跟踪记录,我们只需要对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
# 声明日志的输出级别及输出格式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output ...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t表示 线程名称 %5p 日志级别 %msg 日志信息 \:%m%n 换行
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n
MyBatis作为一个ORM框架,在进行数据库操作的时候是需要和数据库连接的,MyBatis支持基于数据库连接池的连接创建方式。
当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为pooled时,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需要进行自定义配置。
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource = new DruidDataSource();
}
}
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<!--POOLED 使用MyBatis内置的连接池实现 -->
<!--mybatis 需要一个连接池工厂 这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
<!--使用了多态-->
<dataSource type="com.MyBatisDemo.utils.DruidDataSourceFactory">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
MyBatis是基于JDBC的封装,使数据库操作更加便捷,MyBatis除了对JDBC操作步骤进行封装外也对其性能进行了优化
缓存,就是存储数据的内存
MyBatis缓存分为一级缓存和二级缓存。
一级缓存也叫做SqlSession缓存,为每个sqlsession单独分配的缓存内存,无需手动开启,可直接使用。
多个sqlsession的缓存是不共享的。
特性:
1:如果多次查询使用的是同一个Sqlsession对象,则第一次查询以后,数据会存放到缓存,后续的查询则直接访问缓存中存储的数据。
2:如果第一次查询完成后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致。
3:当我们进行再次查询时,想要跳过缓存直接查询数据库,则可以通过sqlsession.clearCache();来清除当前SqlSession的缓存。
4:如果第一次查询以后,第二次查询以前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库。
测试代码:
@Test
public void testQueryMemberById() throws IOException {
SqlSession sqlSession = MyBatisUtil.getFactory().openSession();
SqlSession sqlSession2 = MyBatisUtil.getFactory().openSession();
// SqlSession sqlSession = MyBatisUtil.getSqlSession();
// SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
Member member1 = memberDAO.queryMemberById(1);
System.out.println(member1);
member1.setMemberAge(200);
sqlSession.clearCache();
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
Member member2 =memberDAO2.queryMemberById(1);
System.out.println(member2);
}
二级缓存也成为SqlSessionFactory级缓存。通过同一个factory对象获取的SqlSession可以共享二级缓存;
在应用服务器中SqlSessionFactory是单例的,因此我们二级缓存可以实现全局共享。
特性:
1:二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启
2:二级缓存只能缓存实现了序列化接口的对象
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<cache/>
//该标签中有一些属性可以自行进行配置
//例如
淘汰策略属性: eviction = "FIFO" 先进先出
更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
缓存区的大小属性:size = "223" 多少个对象的引用
只读属性: readOnly = "true" 只能读不能改
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member implements Serializable {
private int memberId;
private String memberNick;
private String memberGender;
private Integer memberAge;
private String memberCity;
}
@Test
public void testQueryMemberById() throws IOException {
SqlSessionFactory factory = MyBatisUtil.getFactory();
//1:多个sqlsession对象来自于同一个sqlsessionFactory
SqlSession sqlSession = factory.openSession(true);
SqlSession sqlSession2 = factory.openSession(true);
MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
Member member1 = memberDAO.queryMemberById(1);
System.out.println(member1);
// sqlSession.clearCache();
sqlSession.commit(); //2:第一次查询之后,执行sqlSession.commit() 会将当前sqlsession的查询结果缓存到二级缓存
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
Member member2 =memberDAO2.queryMemberById(1);
System.out.println(member2);
}
<select id="queryMemberById" resultMap="memberMap" useCache="false">
select member_id,member_nick,member_gender,member_age,member_city
from members
where member_id=#{mid}
</select>
延迟加载——如果在MyBatis中开启了延迟加载执行了子查询(至少查询两次以上),默认只实行第一次查询,当用到子查询的查询结果时,才会出发子查询的执行,如果无需使用子查询结果,则子查询不会执行。
fetchType="lazy"
<?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="com.guoqing.dao.ClassDAO">
<resultMap id="classMap" type="Clazz">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="classDesc"/>
<collection property="stus" select="com.guoqing.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
</resultMap>
<select id="queryClassByCid" resultMap="classMap">
select cid,cname,cdesc from classes where cid = #{cid}
</select>
</mapper>
@Test
public void queryClassByCid() throws IOException {
ClassDAO classDAO = MyBatisUtil.getMapper(ClassDAO.class);
Clazz clazz = classDAO.queryClassByCid(1);
System.out.println(clazz.getClassName());
System.out.println(clazz.getStus());
}