程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

Java学习-Mybatis

发布于2021-03-10 18:22     阅读(739)     评论(0)     点赞(13)     收藏(1)


Mybatis

Maven依赖:

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

持久层

持久化就是将持久状态和瞬时状态转化的过程

数据库(JDBC),IO文件持久化

1、第一个Mybatis程序

1.1、基础配置

  • 编写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://;ocalhost:3306/smbms?serverTimezone=UTC&amp;userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="handhand"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  • 编写Mybatis工具类
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

1.2、编写代码

  • 实体类
public class User {
    private int id;
    private String name;
    private String password;

    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • Dao接口
public interface UserDao {
    public List<User> getUserList();
}
  • 接口实现类,由原来的UserDaoImpl转变为Mapper配置文件
<?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">
<!--命名空间,绑定一个Dao接口-->
<mapper namespace="com.darker.dao.UserDao">
    <select id="getUserList" resultType="com.darker.pojo.User" >
        select * from smbms.user
    </select>

</mapper>

1.3测试

注意点:org.apache.ibatis.binding.BindingException: Type interface com.darker.dao.UserDao is not known to the MapperRegistry.

<!--每一个Mapper.xml都需要在Mybatis的核心配置文件中注册-->
    <mappers>
        <mapper resource="com/darker/dao/UserMapper.xml"/>
    </mappers>
  • 测试代码
public class UserDaoTest {
    @Test
    public void test() {
        //获得session对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一:getMapper执行sql
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭SqlSession
        sqlSession.close();
    }
}

2、CRUD

1、namespace

namespace中的报名要喝Da/mapper接口的包名一致

2、select

选择,查询语句:

  • id:对应namespace接口中的方法名

  • resultType:Sql语句执行的返回值

  • parameterType:参数类型

    <select id="getUserList" resultType="com.darker.pojo.User">
        select *
        from user
    </select>
    

3、insert

<insert id="addUser" parameterType="com.darker.pojo.User">
    insert into user
    values (#{id}, #{name}, #{password});
</insert>

4、update

<update id="updateUser" parameterType="com.darker.pojo.User">
    update user
    set name     = #{name},
        password = #{password}
    where id = #{id};
</update>

5、delete

<delete id="deleteUser" parameterType="int">
    delete
    from user
    where id =
          #{id}
</delete>

6、注意点

增删改需要sqlSession.commit();提交事物。

7、Map

假设实体类中的字段过多,考虑使用Map。

@Test
public void addUser2() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> hashMap = new HashMap<String, Object>();
    hashMap.put("userid",5);
    hashMap.put("username","safasdf");
    hashMap.put("userpassword","2341212");
    mapper.addUser2(hashMap);
    sqlSession.close();
}
<insert id="addUser2" parameterType="map">
    insert into user
    values (#{userid}, #{username}, #{userpassword});
</insert>
//新增用户
int addUser2(HashMap<String, Object> map);

8、模糊查询

在传参是使用%%

List<User> userList = mapper.getUserLikeList("%测试%");

SQL中写

select * from user where name like "%"#{value}"%"

3、配置解析

1、核心配置文件

  • mybatis-config.xml
configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
        environment(环境变量)
            transactionManager(事务管理器)
            dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

1.1、环境配置(environments)

MyBatis 可以配置成适应多种环境,但每个 SqlSessionFactory 实例只能选择一种环境。

Mybatis默认的事物管理器是JDBC,连接池:POOLED;

1.2、属性(properties)

可以通过properties属性来实现引用配置文件【db.properties】

<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="handhand"/>
</properties>
  • 可以引入外部配置文件

  • 可以在其中增加属性配置

  • 如果有重复的字段,优先使用外部配置文件

1.3、类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写

  • 给实体类起别名
<typeAliases>
    <typeAlias type="com.darker.pojo.User" alias="User"/>
</typeAliases>
  • 扫描实体类的包,他的默认别名就为这个类名的首字母小写
<typeAliases>
    <package name="com.darker.pojo">
</typeAliases>
@Alias("helloUser")

1.4、设置(settings)

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

1.5、映射器(mappers)

注册绑定我们的mapper文件

<!--每一个Mapper.xml都需要在Mybatis的核心配置文件中注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

1.6、生命周期和作用域

SqlSessionFactoryBuilder

  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 局部变量

SqlSessionFactory

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • SqlSessionFactory 的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完后关闭

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

  • 实体

    private int id;
    private String name;
    private String pwd;//与数据库不对应
    

    数据库字段

    id
    name
    password 
    

解决方法

  • 起别名
<select id="getUserById" resultType="com.darker.pojo.User" parameterType="int">
    select id,name,password as pwd
    from user
    where id = #{id}
</select>
  • resultMap

结果集映射

<resultMap id="UserMap" type="com.darker.pojo.User">
    <!--column数据库中的字段,property实体类中的属性-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="password" property="pwd"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
    select *
    from user
    where id = #{id}
</select>

5、日志

5.1日志工厂

Mybatis提供的日志工厂

image-20210305102702889

  • SLF4J
  • LOG4J
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING
  • NO_LOGGING

STDOUT_LOGGING标准日志输出

Opening JDBC Connection
Created connection 1073763441.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
==>  Preparing: select * from user where id = ?
==> Parameters: 4(Integer)
<==    Columns: id, name, password
<==        Row: 4, q3142q测试, 23131
<==      Total: 1
User{id=4, name='q3142q测试', pwd='23131'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
Returned connection 1073763441 to pool.

5.2、Log4j

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/darker.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{yyyy-MM-dd HH:mm:ss}][%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

简单使用

1.导包

import org.apache.log4j.Logger;

2.日志对象,参数为当前类的class

Logger logger = Logger.getLogger(UserDaoTest.class);

3.使用

logger.info("info:进入testLog4j");
logger.debug("debug:进入testLog4j");
logger.error("error:进入testLog4j");

6、分页

减少数据的处理量

使用Limit分页

select * from user limit startIndex,pageSize

使用RowBounds

SqlSession sqlSession = MybatisUtils.getSqlSession();
//offset,limit.从查询的第2行开始数,往后面四行数据
RowBounds rowBounds = new RowBounds(1, 4);

List<User> userLise = sqlSession.selectList("com.darker.dao.UserMapper.getUserByRowBounds", null, rowBounds);

for (User user : userLise) {
    System.out.println(user.toString());
}
sqlSession.close();

7.注解开发

在接口上实现

@Select("select * from user")
List<User> getUserListAnn();

配置文件中绑定接口

<mapper class="com.darker.dao.UserMapper"/>

方法中如果有多个参数,一定要使用

@Param("id")
  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加上
  • 如果只有一个基本来兴,可以忽略

8、多对一的处理

需求:查询学生对应的老师信息

  • 学生实体类
private long id;
private String name;
//关联老师
private Teacher teacher;
  • 老师实体类
private long id;
private String name;

按照查询嵌套处理

<select id="getStdentAllInfo" resultMap="StudentTeacher">
    select s.id, s.name, s.tid
    from student s
</select>

<resultMap id="StudentTeacher" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- 复杂的属性,需要单独处理
    association:对象
    collection:集合
    -->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
    select *
    from teacher where id = #{id}
</select>

按照结果嵌套处理

<select id="getStdentAllInfo2" resultMap="StudentTeacher2">
    select s.id sid, s.name sname, t.name tname
    from student s
             left join teacher t on s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
    <result  property="id" column="sid"/>
    <result  property="name" column="sname"/>
    <association  property="teacher" javaType="Teacher">
        <result  property="name" column="tname"/>
    </association>
</resultMap>

9、一对多的处理

需求:查询老师下的学生信息

  • 老师实体类
private long id;
private String name;
private List<Student> students;
  • 学生实体类
private long id;
private String name;
private int tid;

按照查询嵌套处理

<select id="getTeacherAllInfo2" resultMap="TeacherSdudent2">
    select t.id tid, t.name tname
    from  teacher t
</select>
<resultMap id="TeacherSdudent2" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <collection property="students" column="tid" javaType="ArrayList" ofType="Student" select="getStudent"/>
</resultMap>
<select id="getStudent"  resultType="Student">
    select * from student where tid = #{tid}
</select>

按照结果嵌套处理

<!--案结果嵌套查询-->
    <select id="getTeacherAllInfo" resultMap="TeacherSdudent">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from student s,
             teacher t
        where s.tid = t.id
    </select>
    <resultMap id="TeacherSdudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
<!--  javaType:指定属性的类型
     集合中的泛型信息,用ofType获取-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

小结

1.关联 association【多对一】

2.集合 collection 【一对多】

3.javaType 和 ofType

​ javaType 用来指定实体类中的属性类型

ofType 用来指定映射到List或者集合中的POJO类型,泛型中的约束类型

10、动态SQL

在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

if标签中tes属性必输

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog where 1=1
    <if test="title != null">
        and title like #{title}
    </if>
    <if test="author != null">
        and author like #{author}
    </if>
</select>

choose (when, otherwise)

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<select id="queryBlogChoose" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                and title like #{title}
            </when>
            <when test="author != null">
                and author like #{author}
            </when>
            <otherwise>
                1=1
            </otherwise>
        </choose>
    </where>
</select>

trim (where, set)

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。

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

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

image-20210307152224232

<select id="queryBlogForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <foreach item="id" collection="ids" open="and (" separator="or" close=")">
           id = #{id}
        </foreach>
    </where>
</select>

sql拼接结果:select * from blog WHERE ( id = ? or id = ? )

SQL片段

将公共的部分抽取出来,方便复用

使用sql标签抽取公共部分

在要用的时候是用include标签引用

<sql id="if-title-authoe">
    <if test="title != null">
        and title like #{title}
    </if>
    <if test="author != null">
        and author like #{author}
    </if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog where 1=1
    <include refid="if-title-authoe"/>
</select>

11、缓存

Mybatis缓存

  • mybatis默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启,(Sqlsession级别的缓存,也成为本地缓存)
    • 二级缓存需要手动开启,他是namespace级别的环境
    • Mybatis还定义了缓存接口Cache,可以通过实现Cache接口来定义二级缓存

一级缓存

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
  • 手动清除缓存sqlSession.clearCache();

二级缓存

要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行

<cache/>

这些属性可以通过 cache 元素的属性来修改。比如:

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

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,(也就是一级缓存SqlSession .close()之后)但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

原理

用户查询的时候,首先查看二级缓存中有没有,

如果没有则去查看一级缓存有没有,

没有就会去查询数据库。

自定义缓存ehcache

ehcache是一个分布式缓存

依赖:

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

原文链接:https://www.cnblogs.com/darkerguo/p/14498669.html



所属网站分类: 技术文章 > 博客

作者:Djdj

链接:http://www.javaheidong.com/blog/article/112195/2bff1136df01e8e8fffd/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

13 0
收藏该文
已收藏

评论内容:(最多支持255个字符)