它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低。
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.10version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.10version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>RELEASEversion>
<scope>compilescope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.6version>
dependency>
db.url=jdbc:mysql://1.117.173.XXX:3306/db?useUnicode=true&characterEncoding=utf-8&erverTimezone=UTC&useSSL=false
db.user=root
db.password=123456
db.driver=com.mysql.jdbc.Driver
log4j.rootLogger=DEBUG,CONSOLE,file
#log4j.rootLogger=ERROR,ROLLING_FILE
log4j.logger.cn.siliang.dao=debug
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug
log4j.logger.org.tuckey.web.filters.urlrewrite.UrlRewriteFilter=debug
# Console Appender
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=error
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n
# DailyRolling Fil
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern=yyyy-MM-dd
log4j.appender.file.File=log.log
log4j.appender.file.Append=true
log4j.appender.file.Threshold=error
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L)%m%n
<configuration>
<properties resource="jdbc.properties">properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="autoMappingBehavior" value="FULL"/>
settings>
<typeAliases>
<package name="com.test.pojo"/>
typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
plugin>
plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="./mapper/StudentMapper.xml"/>
mappers>
configuration>
public interface StudentMapper {
List<Student> findAll();
int insert(Student student);
int delete(Integer id);
List<Student> findByName(String name);
List<Student> batchFind(int [] arr);
int updata(Student student);
List<Student> findByInfo(Student student);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.StudentMapper">
<sql id="studentColumn">
id
,name,score,age,gender
sql>
<insert id="insert">
insert into student(name, score, age, gender)
values (#{name}, #{score}, #{age}, #{gender});
<selectKey keyProperty="id" order="AFTER" resultType="int">
select LAST_INSERT_ID();
selectKey>
insert>
<update id="updata">
update student
<set>
<if test="name != null and name != ''">
name = #{name},
if>
<if test="age != null and age gt 0 and age lte 100">
age = #{age},
if>
set>
where id = #{id}
update>
<delete id="delete">
delete
from student
where id = #{id};
delete>
<select id="findAll" resultType="com.test.pojo.Student">
select
<include refid="studentColumn">include>
from student;
select>
<select id="findByName" resultType="com.test.pojo.Student">
select *
from student
where name like '%${value}%';
select>
<select id="findByInfo" parameterType="com.test.pojo.Student" resultType="com.test.pojo.Student">
select
<include refid="studentColumn">include>
from student
<where>
<choose>
<when test="id != null">
and id = #{id}
when>
<when test="name != null">
and name = #{name}
when>
<otherwise>
and age = 18
otherwise>
choose>
where>
select>
<select id="batchFind" resultType="com.test.pojo.Student" parameterType="java.lang.Integer">
select * from student
<where>
<if test="array != null and array.length > 0">
and id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
foreach>
if>
where>
select>
mapper>
public class Test {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}
@Test
public void test() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.findAll();
studentList.forEach(System.out::println);
}
@Test
public void test1() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = new Student();
student.setName("zs");
student.setAge(18);
student.setScore(1);
student.setGender(1);
System.out.println(mapper.insert(student));
System.out.println(student.getId());
sqlSession.commit();
}
@Test
public void test2() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(mapper.delete(7));
sqlSession.commit();
}
@Test
public void test3() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.findByName("李状");
list.forEach(System.out::println);
}
@Test
public void test4() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.batchFind(new int[]{1,2,3,4,5,6});
list.forEach(System.out::println);
}
@Test
public void test5() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(mapper.updata(new Student(9, "zsss", 0, 0, 0)));
sqlSession.commit();
}
@Test
public void test6() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.findByInfo(new Student(null, null, 0, 0, 0));
list.forEach(System.out::println);
}
}
全局配置文件中,各个标签要按照如下顺序进行配置,因为mybatis加载配置文件的源码中是按照这个顺序进行解析的。
<configuration>
configuration>
一般会采用#{}
,#{}
在mybatis中,最后会被解析为?,作为占位符,其实就是Jdbc的PreparedStatement中的?
占位符,它有预编译的过程,会对输入参数进行类型解析(如果入参是String类型,设置参数时会自动加上引号),可以防止SQL注入。
如果parameterType属性指定的入参类型是简单类型的话(简单类型指的是8种java原始类型再加一个String),#{}中的变量名可以任意,如果入参类型是pojo,比如是Student类那么#{name}
表示取入参对象Student中的name属性,#{age}
表示取age属性,这个过程是通过反射来做的,这不同于${}
,${}
取对象的属性使用的是OGNL(Object Graph Navigation Language)表达式。
而${}
,一般会用在模糊查询的情景,比如SELECT * FROM student WHERE name like '%${name}%';
它的处理阶段在#{}
之前,它不会做参数类型解析,而仅仅是做了字符串的拼接,若入参的Student对象的name属性为zhangsan,则上面那条SQL最终被解析为SELECT * FROM student WHERE name like '%zhangsan%';
而如果使用的是#{}
,这条SQL最终就会变成SELECT * FROM student WHERE name like '%'zhangsan'%';
所以模糊查询只能用${}
。
虽然普通的入参也可以用${}
,但由于${}
不会做类型解析,就存在SQL注入的风险。比如,虽然普通的入参也可以用${}
,但由于${}
不会做类型解析,就存在SQL注入的风险,因为OR '1' = '1'
恒成立,这样攻击者在不需要知道用户名和密码的情况下,也能够完成登录验证。
另外,对于pojo的入参,${}
中获取对象属性的语法和#{}
几乎一样,但${}
在mybatis底层是通过OGNL表达式语言进行处理的,这跟#{}
的反射处理有所不同。对于简单类型(8种java原始类型再加一个String)的入参,${}
中参数的名字必须是value
,SELECT count(1) FROM
user WHERE name like '%${value}%'
。
默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库。
一级缓存在下面情况会被清除:
默认关闭,可通过全局配置文件中的开启二级缓存总开关,然后在某个具体的mapper.xml中增加,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中
Stu
@Data
public class Stu {
private Integer id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
//老师对学生 -对多
private Integer tid;
}
Teacher
@Data
public class Teacher {
private Integer id;
private String name;
//一个老师多个学生
private List<Stu> stus;
}
public interface StuMapper {
List<Stu> findAll();
List<Stu> findAll2();
}
public interface TeacherMapper {
public Teacher getTeacher(Integer id);
}
StuMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.StuMapper">
<select id="findAll" resultType="stu" resultMap="StuTeacher">
select *
from stu
select>
<resultMap id="StuTeacher" type="stu">
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="teacher">
select *
from teacher
where id = #{id}
select>
<select id="findAll2" resultType="stu" resultMap="StuTeacher2">
select
s.id sid, s.name sname, t.id tid, t.name tname
from stu s
join teacher t
on s.tid = t.id
select>
<resultMap id="StuTeacher2" type="stu">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
association>
resultMap>
mapper>
Teacher.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStu">
select
t.id tid, t.name tname, s.id sid, s.name sname
from stu s
join teacher t on s.tid = t.id
where t.id = #{id}
select>
<resultMap id="TeacherStu" type="teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="stus" ofType="stu">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
collection>
resultMap>
mapper>
public class Test{
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}
/**
* 一对多
*/
@Test
public void test() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StuMapper mapper = sqlSession.getMapper(StuMapper.class);
List<Stu> list = mapper.findAll();
list.forEach(System.out::println);
}
/**
* 一对多
*/
@Test
public void test1() {
SqlSession sqlSession = sqlSessionFactory.openSession();
StuMapper mapper = sqlSession.getMapper(StuMapper.class);
List<Stu> list = mapper.findAll2();
list.forEach(System.out::println);
}
/**
* 多对一
*/
@Test
public void test2() {
SqlSession sqlSession = sqlSessionFactory.openSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
/**
* 分页
*/
@Test
public void test3() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//开启分页
PageHelper.startPage(1, 2);
StuMapper mapper = sqlSession.getMapper(StuMapper.class);
List<Stu> list = mapper.findAll2();
PageInfo<Stu> pageInfo = new PageInfo<>(list);
System.out.println("获取总数:" + pageInfo.getTotal());
list.forEach(System.out::println);
}
}
Mapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
/**
* 分页
*/
@Test
public void test3() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//开启分页
PageHelper.startPage(1, 2);
StuMapper mapper = sqlSession.getMapper(StuMapper.class);
List list = mapper.findAll2();
PageInfo pageInfo = new PageInfo<>(list);
System.out.println("获取总数:" + pageInfo.getTotal());
list.forEach(System.out::println);
}
}