ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MyBatis

2021-04-27 14:02:06  阅读:133  来源: 互联网

标签:缓存 UserMapper log4j sqlSession MyBatis id select


Mybatis程序示例

搭建数据库

CREATE TABLE USER(
id INT(20) NOT NULL,
NAME VARCHAR(30) DEFAULT NULL,
pwd VARCHAR(30) DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
insert into `user`(`id`,`name`,`pwd`) values (1,'dwx','123456'),(2,'dyy','1223');

导入Myabtis和mysql-connector-java的jar包

 <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.6</version>
</dependency>
<dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.22</version>
</dependency>

编写Mybatis核心配置文件

<?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 default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?
useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="13476110270dwx"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/deng/Mapper/userMapper.xml"/>
</mappers>
</configuration>

编写Mybatis工具类

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try{
            //创建sqlSessionFactory对象
            String resource="mybatis-config.xml";
            InputStream is= Resources.getResourceAsStream(resource);
            sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    //获取连接
    public  static SqlSession getSession(){
        return  sqlSessionFactory.openSession();
    }
}

创建实体类

public class User {
    private int id;
    private String name;
    private String pwd;
}

创建Mapper接口

public interface UserMapper {
    List<User> selectUser();
}

编写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.deng.Mapper.UserMapper">
    <select id="selectUser" resultType="com.deng.entity.User">
        select * from user
    </select>
</mapper>

程序可能扫描不到该xml文件,配置pom.xml文件即可

 <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

CURD操作

select,insert,update,delete都具有如下属性:

  • id:与接口中的方法名对应
  • parameterType:传入Sql语句的参数类型
  • resultType:sql语句返回值类型

insert,update,delete都需要session.commit()手动提交事务,否则操作不会提交到数据库

小结

  • 所有的增删改操作都必须提交事务
  • 接口中的参数都写上@Param
  • 可以使用map来传递参数

Mybatis配置解析

  • mybatis-config.xml是Mybatis的核心配置文件
  • 配置内容如下

  • 可以不配置元素,但是需要配置的元素的顺序必须一致,不然会报错。

 

 

 environments元素

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="13476110270dwx"/>
            </dataSource>
        </environment>
    </environments>
  • 配置Mybatis的多套运行环境,必须指定一个为默认的运行环境
  • 子元素节点environment
    • 具体的一套环境,id唯一标识
    • 子元素节点:transactionManager事务管理器
    • 子元素节点:dataSource数据源
      • 使用标准的JDBC数据源接口来配置JDBC连接对象的资源
      • 必须配置数据源
      • 有三种内建的数据源类型
        •  type="[UNPOOLED|POOLED|JNDI]")
        • UNPOOLED:每次被请求时都要打开和关闭来连接
        • POOLED:池,不用频繁打开和关闭连接
        • JNDI:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以 集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
      • 数据源也有很多第三方实现,如DBCP,C3P0

mappers元素

  • 映射器:定义映射sql语句文件
  • 告诉MyBatis去哪里找映射文件

Properties优化

把一些属性配置在priperties文件中。

创建db.properties文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username=root
password=13476110270dwx

将文件导入properties

<properties resource="db.properties"></properties>

typeAliases优化

给java类型设置一个别名

<typeAliases>
     <typeAlias type="com.deng.entity.User" alias="User"/>
</typeAliases>

配置之后,在任何地方,都可以使用User来代替com.deng.entity.User

也可指定包名

<typeAliases>
     <package name="com.deng.entity"/>
</typeAliases>

在com.deng.entity中的每一个JavaBean,会使用首字母小写的类名来作为其别名

生命周期与作用域

Mybtis执行过程

 

 

 作用域分析

  • SqlSessionFactoryBuilder的作用在于创建SqlSessionFactory,创建成功后就没用了,所有其作用域应该时方法作用域
  • SqlSessionFactory的作用是创建一个SqlSession对象,SqlessionFactory的生命周期应该等同于MyBatis的生命周期
  • SqlSessionFactory作为一个单例被应用共享,其作用域为应用作用域
  • SqlSession用来执行sql,应该存活在一个业务请求中,因此其最佳作用域是请求或方法作用域

ResultMap

解决属性名和字段名不一致的问题

当sql语句查询出来的列名于实体类属性列名不一致时就无法映射,使用resultMap来指定映射关系

<mapper namespace="com.deng.Mapper.UserMapper">
    <select id="selectUser" resultMap="userMap">
        select * from user
    </select>
    <resultMap id="userMap" type="com.deng.entity.User">
        <id column="id" property="id"></id>
        <!--column是查询出的数据库列名,property是对于的实体类的属性名-->
        <result column="name" property="name"></result>
        <result column="pwd" property="password"></result>
    </resultMap>
</mapper>

分页的实现

日志工厂

MyBatis可以在控制输出日志信息

标准日志实现

<settings>
        <setting name="logImpl" value="STDOUT_LOGGING" />
</settings>

Log4j

可以控制日志输出的目的地:控制台,文本等;只需要配置即可

步骤:

导入log4j的包

 <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>

编写配置文件

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下
面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/log.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

设置日志实现

<settings>
        <setting name="logImpl" value="LOG4J" />
</settings>

Limit分页

在SQL层面实现分页

<mapper namespace="com.deng.Mapper.UserMapper">
    <select id="selectUser" parameterType="map" resultType="com.deng.entity.User">
        select * from USER limit #{startIndex},#{pageSize}
    </select>
</mapper>

RowBound分页

在java层面实现分页

  public void test1(){
        SqlSession sqlSession= MybatisUtils.getSession();
        int currentpage=1;
        int pageSize=2;
        RowBounds rowBounds=new RowBounds((currentpage-1)*pageSize,pageSize);
        List<User> users=sqlSession.selectList("com.deng.Mapper.UserMapper.selectUser",null,rowBounds);
        for (User user:users
             ) {
            System.out.println(user);
        }
        sqlSession.close();
    }

PageHelper

使用注解开发

使用注解开发就无需mapper.xml映射文件;sql类型注解为:

  • select()
  • update()
  • insert()
  • delete()

步骤:

在接口方法上添加注解

public interface UserMapper {
    @Select("select * from user")
    List<User> selectUser();
}

在mybatis配置文件中注入

<mappers>
       <mapper class="com.deng.Mapper.UserMapper"/>
</mappers>

测试

public void test1(){
        SqlSession sqlSession= MybatisUtils.getSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        List<User> userList=userMapper.selectUser();
        for (User user:userList
             ) {
            System.out.println(user);
        }
       sqlSession.close();
}

注解增删改

public interface UserMapper {
    //查
    @Select("select * from user where id=#{id}")
    User selectById(@Param("id")int id);
    //增
    @Insert("insert into user (id,name,pwd) values(#{id},#{name},#{pwd})")
    int addUser(User user);
    //改
    @Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
    int updateUser(User user);
    //删
    @Delete("delete from user where id=#{id}")
    int deleteById(@Param("id")int id);
}

测试

  @Test
    public void test1() {
        SqlSession sqlSession = MybatisUtils.getSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user1=userMapper.selectById(1);
        System.out.println(user1);
        User user=new User(4,"zzy","hffjhsajfsdhf");
        int res1=userMapper.addUser(user);
        int res2=userMapper.updateUser(user);
        int res3=userMapper.deleteById(4);
        System.out.println(res1);
        System.out.println(res2);
        System.out.println(res3);
    }

@Param注解

@Param注解用于给参数起了一个名字,在方法接收多个参数时使用。如果参数是JavaBean则不能使用

#{}与${}的区别

  • #{}代表占位符
  • ${}代表字符串替换

多对一处理

多个学生对应一个老师

创建数据库

CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, '老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

查询所有学生和对应的老师

查询嵌套

类似SQL中的子查询

<mapper namespace="com.deng.Mapper.StudentMapper">
    <select id="getStudents" resultMap="StudentTeacher">
        select * from student
    </select>
    <resultMap id="StudentTeacher" type="com.deng.entity.Student">
        <association property="teacher" column="tid" javaType="com.deng.entity.Teacher" select="getTeacher"></association>
    </resultMap>
    <select id="getTeacher" resultType="com.deng.entity.Teacher">
        select * from teacher
    </select>
</mapper>

 

在Mybatis核心配置文件配置StudentMapper.xml即可测试

结果嵌套

类似SQL中的联表查询

<mapper namespace="com.deng.Mapper.StudentMapper">
   <select id="getStudents" resultMap="StudentTeacher">
       select s.id sid,s.name sname,t.name tname  from student s,teacher t where s.tid=t.id
   </select>
    <resultMap id="StudentTeacher" type="com.deng.entity.Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"></result>
        <association property="teacher" javaType="com.deng.entity.Teacher">
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
</mapper>

 

一对多处理

一个老师拥有多个学生

查询嵌套

<mapper namespace="com.deng.Mapper.TeacherMapper">
    <select id="getTeacher" parameterType="Integer" resultMap="TeacherStudent">
         select * from teacher where id=#{id}
    </select>
    <resultMap id="TeacherStudent" type="com.deng.entity.Teacher">
        <collection property="students" javaType="ArrayList" ofType="com.deng.entity.Student" column="id" select="getStudentsTeacherId"></collection>
    </resultMap>
    <select id="getStudentsByTeacherId" resultType="com.deng.entity.Student">
        select * from student where tid=#{id}
    </select>
</mapper>

结果嵌套

<mapper namespace="com.deng.Mapper.TeacherMapper">
    <select id="getTeacher" parameterType="Integer" resultMap="TeacherStudent">
         select t.id tid,t.name tname,s.name sname from teacher t,student s where t.id=s.tid and t.id=#{id}
    </select>
    <resultMap id="TeacherStudent" type="com.deng.entity.Teacher">
        <id property="id" column="tid"></id>
        <result property="name" column="tname"></result>
        <collection property="students" ofType="com.deng.entity.Student">
            <result property="name" column="sname"></result>
        </collection>
    </resultMap>
</mapper>

小结

  • association用于一对一和多对一
  • collection用于一对多
  • JavaType和ofType都是用来指定对象类型的
    • JavaType用来指定实体类中属性的类型
    • ofType用来指定List集合中元素的实体类属性类型

动态SQL

If语句

<select id="queryBlogIf" parameterType="Map" resultType="com.deng.entity.Blog">
        select * from blog where
        <if test="title!=null">
            title=#{title}
        </if>
        <if test="author!=null">
            and author=#{author}
        </if>
</select>

如果title为空的话语句就变成 select * from blog where and author=#{author},这是不对的,使用where标签解决

where

<select id="queryBlogIf" parameterType="Map" resultType="com.deng.entity.Blog">
        select * from blog
        <where>
            <if test="title!=null">
                title=#{title}
            </if>
            <if test="author!=null">
                and author=#{author}
            </if>
        </where>
 </select>

如果where标签包含的标签中有返回值就会插入一个where,并且如果返回的内容是AND或者OR开头的就会将它删除

set

<update id="updateBlog" parameterType="Map">
        update blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </set>
        where  id=#{id}
 </update>

choose

<select id="queryBlogChoose" parameterType="Map" resultType="com.deng.entity.Blog">
        select * from blog
        <where>
           <choose>
                 <when test="title!=null">
                    title=#{title}
                </when>
                <when test="author!=null">
                    author=#{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
           </choose>
        </where>
</select>

choose when otherwise会按顺序选择执行,如果第一个条件满足则choose结束,否则判断第二个条件,如果都不满足就选择otherwise

SQL片段

重用SQL代码

    <sql id="repeatCode">
        <if test="title!=null">
            title=#{title}
        </if>
        <if test="author!=null">
            and author=#{author}
        </if>
    </sql>
    <select id="queryBlogIf" parameterType="Map" resultType="com.deng.entity.Blog">
        select * from blog
        <where>
            <include refid="repeatCode"></include>
        </where>
    </select>

foreach

   <select id="queryBlogForeach" parameterType="Map" resultType="com.deng.entity.Blog">
        select * from blog
        <where>
        <foreach collection="ids" item="id" open="and(" close=")" separator="or">
            id=#{id}
        </foreach>
        </where>
    </select>
  • collection:指定输入对象中的集合属性
  • item:每次遍历生成的对象
  • open:开始遍历时的拼接字符串
  • close:结束时拼接的字符串
  • separator:遍历对象之间需要拼接的字符串
  • select * from blog where 1=1 and (id=1 or id=2 or id=3)

 缓存

MyBatis缓存默认定义两级缓存:一级缓存和二级缓存

  • 默认情况,只有一级缓存开启(SqlSession级别的缓存,也称本地缓存)
  • 二级缓存需要手动开启金额配置,是基于namespace级别的缓存
  • Mybatis定义了Cache接口,可以通过实现Cache接口自定义二级缓存

一级缓存

  • 也叫本地缓存:
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 之后如果需要获取相同的数据,直接从缓存中取,而不用查询数据库
 public void test1() {
        SqlSession sqlSession = MybatisUtils.getSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //初始查询,往数据库查询数据
        User user1=userMapper.selectById(1);
        //再次查询会从缓存中拿数据,不会查询数据库
        User user2=userMapper.selectById(1);
        //是同一个对象
        System.out.println(user1==user2);
        sqlSession.close();
}

分析日志可以看出结果

一级缓存失效的4种情况

  • 一级缓存是sqlSession级别的缓存,是一直开启的,我们无法关闭

一级缓存失效的4种情况:

  • sqlSession不同:每个sqlSession之间的缓存是相互独立的
 public void test1() {
        //sqlSession1
        SqlSession sqlSession1 = MybatisUtils.getSession();
        //sqlSession2
        SqlSession sqlSession2=MybatisUtils.getSession();
        //获取Mapper
        UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class);
        User user1=userMapper1.selectById(1);
        User user2=userMapper2.selectById(1);
        //由于sqlSession不同,此时查询出的两个对象不是同一个对象
        System.out.println(user1==user2);
        sqlSession1.close(); 
}
  • sqlSession相同,查询条件不同
  • sqlSession相同,查询条件相同,但是两次查询之间进行了增删改操作,就会重新查询数据库
  • 手动清除了一级缓存
public void test1() {
        //sqlSession
        SqlSession sqlSession = MybatisUtils.getSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user1=userMapper.selectById(1);
        //清除一级缓存
        sqlSession.clearCache();
        User user2=userMapper.selectById(1);
        System.out.println(user1==user2);
        sqlSession.close();
}

二级缓存

  • 二级缓存也叫全局缓存,作用域比一级缓存高
  • 基于namespace级别的缓存(对应一个Mapper(不同的sqlSession共享))

机制:

  • 一个会话查询一条数据,该数据就会放在当前会话的一级缓存中
  • 如果当前会话关闭,对应的一级缓存也就关闭了
  • 引入二级缓存,将不同的mapper查询的数据放在对应的二级缓存中,即使关闭会话,也能从二级缓存查询内容

使用:

在MyBatis核心配置文件开启全局缓存

<setting name="cacheEnabled" value="true""></setting>

在每个mapper.xml文件里配置二级缓存

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"></cache>

测试

    public void test1() {
        //sqlSession
        SqlSession sqlSession1 = MybatisUtils.getSession();
        SqlSession sqlSession2 = MybatisUtils.getSession();
        UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class);
        User user1=userMapper1.selectById(1);
        //只有会话提交或者关闭后,一级缓存中的数据才会转到二级缓存
        sqlSession1.close();
        User user2=userMapper2.selectById(1);
        System.out.println(user1==user2);
        sqlSession2.close();
    }

结论

  • 只要开启了二级缓存,对同一个Mapper查询,可以从二级缓存取数据
  • 查出的数据默认是先放在一级缓存中,当会话关闭或者提交后才会将一级缓存中的数据转放在二级缓存中

缓存原理

 

 

 EhCache

第三方缓存实现:java分布式缓存

使用:

导入jar包

<dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.1</version>
</dependency>

在mapper.xml中使用该缓存

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

可以编写encache.xml文件进行配置,否则直接使用默认配置

标签:缓存,UserMapper,log4j,sqlSession,MyBatis,id,select
来源: https://www.cnblogs.com/python-road/p/14706810.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有