MyBatis的基本应用

MyBatis的基本应用

​ 它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低。

  • ORM
    • Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,比如用一个Java的Student类,去对应数据库中的一张student表,类中的属性和表中的列一一对应。Student类就对应student表,一个Student对象就对应student表中的一行数据
  • 为什么mybatis是半自动的ORM框架?
    • 用mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialect),所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多

快速入门

在这里插入图片描述

创建数据库

在这里插入图片描述

添加依赖

<!--mysql连接驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.10</version>
</dependency>
<!--mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.10</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.6</version>
</dependency>

配置文件

jdbc.properties

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.properties

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

mybatis-config.xml

<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>
        <!-- 加载前面编写的SQL语句的文件 -->
        <mapper resource="./mapper/StudentMapper.xml"/>
    </mappers>

</configuration>

Mapper(interface)

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);

}

Mapper(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 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});
        <!--根据LAST_INSERT_ID()函数获得最后一个添加的id,order="AFTER":mysql是添加之后取id-->
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID();
        </selectKey>
    </insert>

    <!--useGeneratedKeys="true":JDBC对于支持自动生成记录主键的数据库,如:MySQL,SQL Server,此时设置useGeneratedKeys参数值为true,
        在执行添加记录之后可以获取到数据库自动生成的主键ID。
        keyProperty: 映射javabean中的主键-->
    <!--<insert id="insert" parameterType="com.test.pojo.Student" useGeneratedKeys="true" keyProperty="id">
        insert into student(name, score, age, gender)
        values (#{name}, #{score}, #{age}, #{gender});
    </insert>-->

    <!--prefix:添加前缀
        suffix:添加后缀
        prefixOverrides:删除前缀
        suffixOverrides:删除后缀-->
    <!--<update id="updata">
        update student
        <trim prefix="set" suffix="where id = #{id}" suffixOverrides=",">
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="age != null and age gt 0 and age lte 100">
                age = #{age},
            </if>
        </trim>
    </update>-->
    <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>
            <!--等同于java中switch-->
            <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
                <!--collection:遍历的集合,item:每次循环中的元素,open:遍历开始时添加,
                close:遍历结束时添加,separator:每个元素中间添加-->
                <foreach collection="array" item="id" open="(" separator="," close=")">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

</mapper>

Test

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>
	<!-- 配置顺序如下
     properties:
			一般将数据源的信息单独放在一个properties文件中,然后用这个标签引入,在下面						environment标签中,就可以用${}占位符快速获取数据源的信息

     settings:
			用来开启或关闭mybatis的一些特性,比如可以用<setting name="lazyLoadingEnabled" 			value="true"/>来开启延迟加载,可以用<settings name="cacheEnabled" 						    value="true"/>来开启二级缓存

     typeAliases:
			起别名。在mapper.xml中需要使用parameterType和resultType属性来配置SQL语句的输入参数				类型和输出参数类型,类必须要写上全限定名,比如一个SQL的返回值映射为Student类,则					resultType属性要写com.xxx.Student,这太长了,所以可以用别名来简化书写。 <package 				name="com.test.pojo"/>,如此,指定包下的所有类,都会以简单类名的小写形式,作为它的别			 名。如此,指定包下的所有类,都会以简单类名的小写形式,作为它的别名。
			另外,对于基本的Java类型 -> 8大基本类型以及包装类,以及String类型,mybatis提供了默认的			  别名,别名为其简单类名的小写,比如原本需要写java.lang.String,其实可以简写为string

     typeHandlers:
			用于处理Java类型和Jdbc类型之间的转换,mybatis有许多内置的TypeHandler,比如					StringTypeHandler,会处理Java类型String和Jdbc类型CHAR和VARCHAR。这个标签用的不多

     objectFactory:
			mybatis会根据resultType或resultMap的属性来将查询得到的结果封装成对应的Java类,它有一			 个默认的DefaultObjectFactory,用于创建对象实例,这个标签用的也不多

     plugins:
			可以用来配置mybatis的插件,比如在开发中经常需要对查询结果进行分页,就需要用到pageHelper			分页插件,这些插件就是通过这个标签进行配置的。在mybatis底层,运用了责任链模式+动态代理去			实现插件的功能

     environments
        environment
            transactionManager
            dataSource
			用来配置数据源

     mappers:
			用来配置mapper.xml映射文件,这些xml文件里都是SQL语句
     -->
</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)的入参,${}中参数的名字必须是valueSELECT count(1) FROM user WHERE name like '%${value}%'

缓存

一级缓存

​ 默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库。
​ 一级缓存在下面情况会被清除:

  • 在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
  • SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
  • 对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
  • 在全局配置文件中设置 ,这样会使一级缓存失效,二级缓存不受影响

二级缓存

​ 默认关闭,可通过全局配置文件中的开启二级缓存总开关,然后在某个具体的mapper.xml中增加,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中

扫描二维码关注公众号,回复: 14710018 查看本文章

关联查询

pojo

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;
}

Interface

public interface StuMapper {
    
    

    List<Stu> findAll();

    List<Stu> findAll2();
}

public interface TeacherMapper {
    
    

    public Teacher getTeacher(Integer id);

}

xml

StuMapper.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 namespace="com.test.dao.StuMapper">

    <!--
        获取所有学生及老师的信息
        思路:
            1.获取所有学生信息
            2.根据获取的学生信息tid来获取其老师的信息
        实施:
            1.做一个结果映射,其结果集的返回值为Stu
            2.通过association来多一对一/对多的结果集映射
    -->
    <select id="findAll" resultType="stu" resultMap="StuTeacher">
        select *
        from stu
    </select>
    <resultMap id="StuTeacher" type="stu">
        <!-- property:stu中的属性名, column:多放的表的列名, javaType:属性类型-->
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
    </resultMap>
    <!--
        这里传过来的id,只有在一个属性的时候,西面可以写任意值
        如果多个属性的情况下,在association中的column多参数配置:
        column="key=value,key=value",
        为键值对形式,可以是传给下个sql的取值名,value是上面片段中sql查询的字段名
    -->
    <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

<?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.test.dao.TeacherMapper">

    <!--
        集合使用collection
            JavaType和ofType都是用来指定对象类型
            JavaType是用来指定pojo中属性的类型
            ofType指定的是映射到list集合属性中pojo的类型
    -->
    <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>

Test(分页)

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<Stu> list = mapper.findAll2();
    PageInfo<Stu> pageInfo = new PageInfo<>(list);
    System.out.println("获取总数:" + pageInfo.getTotal());
    list.forEach(System.out::println);
}

}


猜你喜欢

转载自blog.csdn.net/li6909096/article/details/128981523
今日推荐