MyBatis

MyBatis

SSM

Spring + SpringMVC + MyBatis

  • MyBatis充当Dao层
  • Spring充当润滑油的角色
  • SpringMVC充当Servlet的角色,可以理解为SpringMVC是Spring的Web支持
MybatisPlus:国内的团队,baomidou,可以写SQL,也可以不写SQL。介于Mybatis和Hibernate之间。

Hibernate:充当Dao层。不需要写SQL,自动生成并执行SQL语句。

MyBatis

历史

Mybatis是apache的一个开源项目IBatis,2010年,apache迁移给了Google code,改名Mybatis,2013年迁移到GitHub。

Mybatis3,IBatis1,IBatis2。

作用

是一款优秀的持久层框架,支持定制化的SQL、存储过程以及高级映射。

之前我们学过的所有的JDBC代码和手动设置参数获取结果集,都不用写了。

使用简单的XML配置文件或者注解来映射原生信息,更加的方便。

持久化

持久化把数据存储在磁盘而不是内存。

1. 程序产生的数据首先都是在内存

2. 内存不可靠,我们需要通过一些技术把数据永久存储在硬盘上。

持久层

之前的dao层,之前我们通过反射,通过泛型,通用的dao,我们在操作不同的表或者逻辑的时候,我们之前会创建一个接口,再创建接口的实现类,实际上这个dao层,就叫持久层

优缺点

sql语句与代码分离,存放于xml文件中。(最牛逼的特点)

  优点:便于维护管理,不用在java代码中找sql语句。

  缺点:不能通过打断点的方式调试。通过日志来解决这个问题。

动态SQL语句。(最牛逼的特点)

  优点:通过逻辑标签代替编写逻辑代码,生成不同的SQL。

  缺点:拼接复杂的SQL语句时,没有直接拼接直观。

查询结果和java对象自动映射

  优点:保证名称之间的对应关系。可以下划线和驼峰自动转换。

  缺点:对开发人员的SQL语句依赖很强。

XML配置文件

约束

保证我们的xml能够使用哪些标签,保证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 default="development">
        <environment id="development">
            <!--  事务管理    -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 数据源配置 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1/ssm?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

<!-- 注册各个映射文件   -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>
    
</configuration>

 创建接口

public interface UserMapper {

    // 根据id查询用户
    User selectUserById(Integer id);

}

配置Mapper.xml

第一次使用Mybatis,需要两个文件。

一个接口类mapper(就是咱们之前写的dao)Java文件,不需要我们写`实现类`

一个接口对应着一个xml文件

两个文件的名字最好相同,UserMapper.java->UserMapper.xml

框架会根据mapper和xml联合,通过代理模式创建实现类。

一般情况下,我们管接口对应的xml文件叫做映射文件。

     resultType:结果集的自动映射,必须写,对象属性名和查询结果的列名必须对应上
  parameterType:参数类型,可以自动解析,可以写不写,如果是我们自定义的引用数据类型,建议写上全类名

<?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">
<!-- namespace:对应的mapper接口的全类名 -->
<mapper namespace="com.jsoft.dao.UserMapper">

<!--  写sql语句  -->
<!--
    id:mapper接口中的方法名
    resultType:方法的返回值类型,如果是entity类型,需要写全类名
    parameterType:方法的入参的类型,可以省略
    #{id}:代表方法的入参,类似于之前的?占位符,Mybatis底层使用的是什么?PreparedStatement
  -->
    <select id="selectUserById" resultType="com.jsoft.entity.User">
        select id,username,password from user where id = #{id}
    </select>

</mapper>

注意:在主配置需要注册映射文件

<mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>

测试类

import com.jsoft.dao.UserMapper;
import com.jsoft.entity.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.InputStream;

// 测试我们的UserMapper
public class UserMapperTest {

    @Test
    public void testSelectUserById() {
        // 构建一个session工厂
        // 需要加载mybatis的主配置文件
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        // 获取session
        SqlSession session = sqlSessionFactory.openSession();
        // 获取到接口的代理实现类
        UserMapper mapper = session.getMapper(UserMapper.class);

        // 调接口里的方法
        User user = mapper.selectUserById(1);
        System.out.println(user);
    }
}

 

案例方法

UserMapper接口的方法

    List<User> selectUsersIF(User user);

    List<User> findAllUsersByUsername(String username);

    List<User> findAllUsersByPage(Integer pageNum);

    // 查询所有
    List<User> findAllUsers();

    // 根据id查询用户
    User selectUserById(Integer id);

    // 根据id删除用户
    int deleteUserById(Integer id);

    // 修改用户
    int updateUser(User user);

    // 新增用户
    int saveUser(User user);

UserMapper.xml

模糊查询:

可以用注释的方法直接用

在Mybatis的映射文件中,$和#号的区别?
$底层使用的是Statement,拼串,SQL注入的问题,不安全
#底层使用的是PreparedStatement,预编译,#相当于占位符

如果想要做模糊查询,在我们的java代码层面去解决%的问题。
 service层,这些事都交给我们的service层

<select id="findAllUsersByUsername" resultType="com.jsoft.entity.User">    
--         select id,username,password from user where username like '%${username}%'
            select id,username,password from user where username like #{username}
    </select>

 测试类

    SqlSession session;

    // 在@Test的方法执行之前执行的方法
    @Before
    public void before() {
        // 构建一个session工厂
        // 需要加载mybatis的主配置文件
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    @Test
    public void testFileAllUsersByUsername() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.findAllUsersByUsername("%username%");
        System.out.println(users);
    }

 

查询

UserMapper接口

//分页查询
List<User> findAllUsersByPage(Integer pageNum);

    // 查询所有
    List<User> findAllUsers();

    // 根据id查询用户
    User selectUserById(Integer id);

UserMapper.xml

<select id="findAllUsersByPage" resultType="com.jsoft.entity.User">
        select id,username,password from user limit #{pageNum},8
    </select>
    
    <!--  如果方法的返回值是集合,在映射xml中,resultType应该怎么写?   -->
    <select id="findAllUsers" resultType="com.jsoft.entity.User">
        select id,username,password from user
    </select>
    
    <select id="selectUserById" resultType="com.jsoft.entity.User">
        select id,username,password from user where id = #{id}
    </select>

 测试类

SqlSession session;

    // 在@Test的方法执行之前执行的方法
    @Before
    public void before() {
        // 构建一个session工厂
        // 需要加载mybatis的主配置文件
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    @Test
    public void testFindAllUsersByPage() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.findAllUsersByPage(0);
        System.out.println(users);
    }

    @Test
    public void testFindAllUsers() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.findAllUsers();
        System.out.println(users);
    }

    @Test
    public void testSelectUserById() {
        // 获取到接口的代理实现类
        UserMapper mapper = session.getMapper(UserMapper.class);

        // 调接口里的方法
        User user = mapper.selectUserById(1);
        System.out.println(user);
    }

 DML语句(增加,删除,修改)

UserMapper接口

    // 根据id删除用户
    int deleteUserById(Integer id);

    // 修改用户
    int updateUser(User user);

    // 新增用户
    int saveUser(User user);

 UserMapper.xml

<insert id="saveUser" parameterType="com.jsoft.entity.User">
        insert into user (username,password) values (#{username},#{password})
    </insert>

    <update id="updateUser" parameterType="com.jsoft.entity.User">
        update user set username = #{username},password = #{password} where id = #{id}
    </update> 
    
    <delete id="deleteUserById" parameterType="java.lang.Integer">
        delete from user where id = #{id}
    </delete>

 测试类

在执行DML语句时,可以得到正确的返回值1,但MyBatis不会自动提交事务

在数据库数据发生变化,需要控制事务(新增,修改,删除)

Mybatis有两种方式可以提交事务:
  1、通过session调用commit方法
  2、关闭session

关闭流,关闭socket,关闭连接....

SqlSession session;

    // 在@Test的方法执行之前执行的方法
    @Before
    public void before() {
        // 构建一个session工厂
        // 需要加载mybatis的主配置文件
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    // 在@Test的方法执行之后的方法
    @After
    public void after() {
        try {
            // 事务提交
            session.commit();
            // session关闭
            session.close();
        }catch(Exception e){
            e.printStackTrace();
            // 事务回滚
            session.rollback();
        }
    }

    @Test
    public void testUpdateUser() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        int i = mapper.updateUser(new User(5, "liangchaowei", "987654"));
        System.out.println(i);
    }

    @Test
    public void testSaveUser() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        for (int i = 0; i < 100; i++) {
            mapper.saveUser(new User(null, "username" + i, "password" + 1));

        }
//        System.out.println(i);

    }

    @Test
    public void testDeleteUserById() {
        // 获取到接口的代理实现类
        UserMapper mapper = session.getMapper(UserMapper.class);

        // 调接口里的方法
        int i = mapper.deleteUserById(4);
        System.out.println(i);
    }

 

注解版MyBatis

注解版的Mybatis,不推荐使用

当我们使用的是注解版的Mybatis,也需要在主配置文件中进行注册

为什么不推荐使用注解版的Mybatis???

Mybatis的亮点就是sql语句和Java代码分离,单独存放sql

    @Select("select id,username,password from user")
    List<User> findAllUsers();

    @Select("select id,username,password from user where id = #{id}")
    User selectUserById(Integer id);

    @Delete("delete from user where id = #{id}")
    int deleteUserById(Integer id);

    @Update("update user set username = #{username},password = #{password} where id = #{id}")
    int updateUser(User user);

    @Insert("insert into user values (#{id},#{username},#{password})")
    int saveUser(User user);

 mybatis-config.xml注册映射文件

    <!-- 注册各个映射文件   -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        
        // 注解版MyBatis的注册
        <mapper class="com.jsoft.dao.UserMapper2"></mapper>
    </mappers>

 测试类

    SqlSession session;

    // 在@Test的方法执行之前执行的方法
    @Before
    public void before() {
        // 构建一个session工厂
        // 需要加载mybatis的主配置文件
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    // 在@Test的方法执行之后的方法
    @After
    public void after() {
        try {
            // 事务提交
            session.commit();
            // session关闭
            session.close();
        }catch(Exception e){
            e.printStackTrace();
            // 事务回滚
            session.rollback();
        }
    }

@Test
    public void testUserMapper2() {
        UserMapper2 mapper2 = session.getMapper(UserMapper2.class);
        User user = mapper2.selectUserById(1);
        System.out.println(user);
    }

 

动态sql语句

根据不同的条件来查询数据

如果有username,根据username去查

如果有password,根据password去查

如果两个都有,根据username和password去查询

定义接口

List<User> selectUsersIF(User user);

UserMapper.xml

<where>会识别第一个and并且去掉

<if>也没有else标签

<!--  sql片段  -->
    <sql id="selectUser">
        select id,username,password from user
    </sql>

    <!--  多条件的查询,可以拼成多个条件,只要条件满足,都会看  -->
    <select id="selectUsersIF" resultType="com.jsoft.entity.User">
--         select id,username,password from user
--         where 1 = 1
--      引用sql片段
        <include refid="selectUser"></include>
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null and username !=''">
                and username = #{username}
            </if>
            <if test="password != null and password !=''">
                and password = #{password}
            </if>
        </where>
    </select>

 

定义接口

List<User> selectUsersChoose(User user);

UserMapper.xml

类似于我们之前学过的switch...case,只会走一个条件,如果发现有满足的条件,后面的就不看了

    <!-- 类似于我们之前学过的switch...case,只会走一个条件,如果发现有满足的条件,后面的就不看了 -->
    <select id="selectUsersChoose" resultType="com.jsoft.entity.User">
        select id,username,password from user
        <where>
            <choose>
                <when test="username != null and username !=''">
                    username = #{username}
                </when>
                <when test="password != null and password !=''">
                    password = #{password}
                </when>
                <otherwise>
                    id = #{id}
                </otherwise>
            </choose>
        </where>
    </select>

 测试类

    @Test
    public void testSelectUsersChoose() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUsersChoose(new User(0,"admin","xxxxx"));
        System.out.println(users);
    }

 

定义接口

List<User> selectUsersTrim(User user);

UserMapper.xml

trim去掉一些特殊的sql语法,比如我们常见的and,or,trim元素我们需要去掉的一些特殊的字符串,现在已经很少用了

<!-- trim去掉一些特殊的sql语法,比如我们常见的and,or,trim元素我们需要去掉的一些特殊的字符串,现在已经很少用了 -->
    <select id="selectUsersTrim" resultType="com.jsoft.entity.User">
        select id,username,password from user
--         prefix:前缀 prefixOverrides:覆盖前缀,若if都不执行,会自动加上一个恒等式
        <trim prefix="where" prefixOverrides="and">
            <if test="username != null and username != ''">
                and username=#{username}
            </if>
            <if test="id != null">
                and id = #{id}
            </if>
        </trim>
    </select>

 测试类

    @Test
    public void testSelectUsersTrim() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUsersTrim(new User(null,"","xxxxx"));
        System.out.println(users);
    }

 

定义接口

传参是集合需要让MyBatis知道传入的是集合,加上@Param("")ids 

List<User> selectUserByIds(@Param("ids") List<Integer> ids);

 UserMapper.xml

    <select id="selectUserByIds" resultType="com.jsoft.entity.User">
        select id,username,password from user
        where id in
--      collection:要遍历的集合
--      open:前置的括号(
--      close:后置的括号)
--      separator:每个数据遍历出来的分隔符
--      item:遍历出来的每一项
--      (1,5,6)
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>

    </select>

 测试类

    @Test
    public void testSelectUserByIds() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUserByIds(Arrays.asList(1, 5, 6));
        System.out.println(users);
    }

 

 定义接口

int updateUserSet(User user);

 UserMapper.xml

<set>会识别多余的 "," 

<update id="updateUserSet" parameterType="com.jsoft.entity.User">
        update user
            <set>
                <if test="username != null and username !=''">
                    username = #{username},
                </if>
                <if test="password != null and password !=''">
                    password = #{password}
                </if>
            </set>
        where id = #{id}
    </update>

测试类

    @Test
    public void testUpdateUserSet() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        int i = mapper.updateUserSet(new User(5,"liangjiahui",""));
        System.out.println(i);
    }

 

 sql片段

<!--  sql片段  -->
    <sql id="selectUser">
        select id,username,password from user
    </sql>


--      引用sql片段
        <include refid="selectUser"></include>

 

MyBatis传入多参

MyBatis想让我们封装成一个对象,只传一个,如果传入多个参数,不要超过3个

当Mapper接口传入多个参数时,尽量保证类型的统一

参数类型要么是java内置的数据类型,包装器类型或是String,集合

定义接口

User selectUserByUsernameAndPassword(String username,String password);

UserMapper.xml

<select id="selectUserByUsernameAndPassword" resultType="user1">
        select id,user_name,password from user where user_name = #{username} and password = #{password}
    </select>

测试类

SqlSession session;

    @Before
    public void before() {
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        try {
            session.commit();
            session.close();
        }catch(Exception e){
            e.printStackTrace();
            session.rollback();
        }
    }

    /*
        测试类的类名,以目标类开头,以Test结尾
    *   测试类中的测试方法,尽量以目标方法的方法名结尾,test目标方法名
    * */
    @Test
    public void testSelectUserByUsernameAndPassword() {
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.selectUserByUsernameAndPassword("admin", "123456");
        System.out.println(user);
    }

运行会报错

按照提示修改

<select id="selectUserByUsernameAndPassword" resultType="user1">
        select id,user_name,password from user where user_name = #{arg0} and password = #{arg1}
    </select>

运行成功

1、如果传入的是User对象,在mapper.xml映射文件中,就必须和对象的属性名匹配
2、如果传入的是java内置的数据类型的参数,String,Integer..,如果只传一个参数,直接使用#{参数名},#{param1},#{xxx}
3、如果传入的是java内置的数据类型的参数,String,Integer..,如果传入多个参数,必须使用#{paramN}.#{arg0},不能用#{参数名}

原理:

Mybatis在封装参数的时候,封装成了一个Map集合,

如果传入的是User对象,value:{“username”:"admin","password":"123456","id":"1"}

  【"username":"admin"】,【"password":"123456"】

如果传入的是一个内置类型的参数,字面量  【"xxxx":"admin"】

如果从传入的是多个参数,【"param1":"admin"】,【"param2":"123456"】

  底层封装的map集合,arg0和arg1。起了个默认的别名叫param1,param2

如果传入的是集合,【"param1":"{1,2,3,4,5}"】

需要掌握:mybatis传入参数的策略,封装成了一个map集合

若在xml文件不想用别名

<select id="selectUserByUsernameAndPassword" resultType="user1">
        select id,user_name,password from user where user_name = #{username} and password = #{password}
    </select>

需要定义接口为

User selectUserByUsernameAndPassword(@Param("username") String username,@Param("password") String password);

 

给类取别名

<!--  别名:各类起别名,给实体类起别名  -->
    <!-- 注意事项:typeAliases必须放在environments上面 -->
    <typeAliases>
        <!--  <typeAlias type="com.jsoft.entity.User" alias="user"></typeAlias>-->
        
        <!--  当前包下的所有的实体类都是以类名的首字母小写来设置别名 -->
        <package name="com.jsoft.entity"/>  
        
        <!-- 场景:当前包下的某个类不想以类名小写来当做别名,
            需要在实体类上加注解   @Alias("user1") -->
    </typeAliases>

 

druid连接池数据源配置

db.properties

druid.driverName=com.mysql.jdbc.Driver
druid.url=jdbc:mysql://localhost:3306/ssm?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8
druid.username=root
druid.password=root

mybatis-config.xml

    <!-- 引入外部的资源文件 -->
    <properties resource="db.properties"></properties>

<environments default="development">
        <environment id="development">
            <!--  事务管理    -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 数据源配置 -->
            <!--
                type:
                    1、UNPOOLED:不使用连接池
                    2、POOLED:使用Mybatis默认的连接池
                    3、JNDI:使用JNDI实现的数据源
                    4、使用我们自定义的连接池实现,需要我们去写一个具体的实现类
             -->
            <dataSource type="com.jsoft.datasource.DruidDataSourceFactory">
                <!-- property标签中的name属性只需要按照druid的命名规则命名即可  -->
                <property name="druid.driverName" value="${druid.driverName}"/>
                <property name="druid.url" value="${druid.url}"/>
                <property name="druid.username" value="${druid.username}"/>
                <property name="druid.password" value="${druid.password}"/>
            </dataSource>
        </environment>
    </environments>

创建com.jsoft.datasource.DruidDataSourceFactory

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.DataSourceFactory;

import javax.sql.DataSource;
import java.util.Properties;

public class DruidDataSourceFactory implements DataSourceFactory {

    private Properties properties;

    @Override
    public void setProperties(Properties properties) {
        // 直接把mybatis-config中的参数封装成properties
        this.properties = properties;
    }

    @Override
    public DataSource getDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.configFromPropety(properties);
        return druidDataSource;
    }
}

 

MyBatis其他配置

mybatis-config.xml

<!-- mybatis的其他配置 -->
    <settings>
        <!-- 数据库的字段名以下划线命名的自动转换成小驼峰 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 日志实现:记录发生过的事情,主要是记录在本地,后期运营维护 -->
        <!-- log4j,STDOUT_LOGGING -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

第三方提供的日志框架

pom.xml

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

需要配置文件  log4j.properties

#############
# \u8F93\u51FA\u5230\u63A7\u5236\u53F0
#############

# log4j.rootLogger\u65E5\u5FD7\u8F93\u51FA\u7C7B\u522B\u548C\u7EA7\u522B\uFF1A\u53EA\u8F93\u51FA\u4E0D\u4F4E\u4E8E\u8BE5\u7EA7\u522B\u7684\u65E5\u5FD7\u4FE1\u606FDEBUG < INFO < WARN < ERROR < FATAL
# WARN\uFF1A\u65E5\u5FD7\u7EA7\u522B     CONSOLE\uFF1A\u8F93\u51FA\u4F4D\u7F6E\u81EA\u5DF1\u5B9A\u4E49\u7684\u4E00\u4E2A\u540D\u5B57       logfile\uFF1A\u8F93\u51FA\u4F4D\u7F6E\u81EA\u5DF1\u5B9A\u4E49\u7684\u4E00\u4E2A\u540D\u5B57
log4j.rootLogger=DEBUG,WARN,INFO,ERROR,CONSOLE,logfile
# \u914D\u7F6ECONSOLE\u8F93\u51FA\u5230\u63A7\u5236\u53F0
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
# \u914D\u7F6ECONSOLE\u8BBE\u7F6E\u4E3A\u81EA\u5B9A\u4E49\u5E03\u5C40\u6A21\u5F0F
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 
# \u914D\u7F6ECONSOLE\u65E5\u5FD7\u7684\u8F93\u51FA\u683C\u5F0F  [frame] 2019-08-22 22:52:12,000  %r\u8017\u8D39\u6BEB\u79D2\u6570 %p\u65E5\u5FD7\u7684\u4F18\u5148\u7EA7 %t\u7EBF\u7A0B\u540D %C\u6240\u5C5E\u7C7B\u540D\u901A\u5E38\u4E3A\u5168\u7C7B\u540D %L\u4EE3\u7801\u4E2D\u7684\u884C\u53F7 %x\u7EBF\u7A0B\u76F8\u5173\u8054\u7684NDC %m\u65E5\u5FD7 %n\u6362\u884C
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

################
# \u8F93\u51FA\u5230\u65E5\u5FD7\u6587\u4EF6\u4E2D
################

# \u914D\u7F6Elogfile\u8F93\u51FA\u5230\u6587\u4EF6\u4E2D \u6587\u4EF6\u5927\u5C0F\u5230\u8FBE\u6307\u5B9A\u5C3A\u5BF8\u7684\u65F6\u5019\u4EA7\u751F\u65B0\u7684\u65E5\u5FD7\u6587\u4EF6
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# \u4FDD\u5B58\u7F16\u7801\u683C\u5F0F
log4j.appender.logfile.Encoding=UTF-8
# \u8F93\u51FA\u6587\u4EF6\u4F4D\u7F6E\u6B64\u4E3A\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u7684logs\u6587\u4EF6\u5939\u4E2D
log4j.appender.logfile.File=logs/root.log
# \u540E\u7F00\u53EF\u4EE5\u662FKB,MB,GB\u8FBE\u5230\u8BE5\u5927\u5C0F\u540E\u521B\u5EFA\u65B0\u7684\u65E5\u5FD7\u6587\u4EF6
log4j.appender.logfile.MaxFileSize=10MB
# \u8BBE\u7F6E\u6EDA\u5B9A\u6587\u4EF6\u7684\u6700\u5927\u503C3 \u6307\u53EF\u4EE5\u4EA7\u751Froot.log.1\u3001root.log.2\u3001root.log.3\u548Croot.log\u56DB\u4E2A\u65E5\u5FD7\u6587\u4EF6
log4j.appender.logfile.MaxBackupIndex=3  
# \u914D\u7F6Elogfile\u4E3A\u81EA\u5B9A\u4E49\u5E03\u5C40\u6A21\u5F0F
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

##########################
# \u5BF9\u4E0D\u540C\u7684\u7C7B\u8F93\u51FA\u4E0D\u540C\u7684\u65E5\u5FD7\u6587\u4EF6
##########################

# club.bagedate\u5305\u4E0B\u7684\u65E5\u5FD7\u5355\u72EC\u8F93\u51FA
log4j.logger.club.bagedate=DEBUG,bagedate
# \u8BBE\u7F6E\u4E3Afalse\u8BE5\u65E5\u5FD7\u4FE1\u606F\u5C31\u4E0D\u4F1A\u52A0\u5165\u5230rootLogger\u4E2D\u4E86
log4j.additivity.club.bagedate=false
# \u4E0B\u9762\u5C31\u548C\u4E0A\u9762\u914D\u7F6E\u4E00\u6837\u4E86
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

 

MyBatis多表查询

一对一查询

多表查询需要多个表,创建所需实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee implements Serializable {


    private static final long serialVersionUID = -7668683037448183879L;

    private Integer id;
    private String name;

    private Integer did;
    private Dept dept;

    public Employee(Integer id, String name,Integer did) {
        this.id = id;
        this.name = name;
        this.did = did;
    }
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {


    private static final long serialVersionUID = -2648543219194335591L;

    private Integer id;
    private String name;

    private List<Employee> employees;
}

多表查询的问题

使用SQL99语法!!!
我们的sql语句应该使用左连接还是内连接???
  1、什么情况下使用左连接或者右连接???left join
    无论主表中的数据是否和从表有关联关系,主表中的数据都需要显示出来
  2、什么情况用内连接inner join

规范:
  如果我要的数据,只要有关联的数据,必须用inner join
  如果我要的数据,某一张表的数据无论和另外一张表是否有关联,都需要展示,必须有left join

如果两张表中的数据都是有关联的,推荐使用inner join,效率高于left join

 

创建接口

查询员工信息的同时,部门信息也是封装好的。
Employee findEmployeeById(Integer id);

注册EmployeeMapper.xml

    <!-- 注册各个映射文件   -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        <mapper resource="mapper/EmployeeMapper.xml"></mapper>
        <mapper resource="mapper/DeptMapper.xml"></mapper>
    </mappers>

EmployeeMapper.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.jsoft.dao.EmployeeMapper">
    
        <!-- 定义我的多表查询的结果集 -->
    <resultMap id="employeeResult" type="employee">
        &lt;!&ndash; property:类中的属性名   column:结果集的字段名 &ndash;&gt;
        <id property="id" column="eid"></id>
        <result property="name" column="ename"></result>
        &lt;!&ndash; 配置的是Employee中的dept属性的封装策略 &ndash;&gt;
        <association property="dept" javaType="dept">
            <id property="id" column="did"></id>
            <result property="name" column="dname"></result>
        </association>
    </resultMap>
     resultMap:自定义结果集映射   
    <select id="findEmployeeById" resultMap="employeeResult">
        select e.id,e.name,d.id did,d.name dname
        from employee e
                 inner join dept d
                            on e.did = d.id
        where e.id = #{id}
    </select>
    
    </mapper>

测试类

 SqlSession session;
    SqlSession session1;

    @Before
    public void before() {
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
        session1 = sqlSessionFactory.openSession();
    }

//    @After
//    public void after() {
//        try {
//            session.commit();
//            session.close();
//        }catch(Exception e){
//            e.printStackTrace();
//            session.rollback();
//        }
//    }

    @Test
    public void testFindEmployeeById() {

        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
        Employee employee = mapper.findEmployeeById(3); 
    }
}

 

多表查询方式二:分布查询

EmployeeMapper.xml

    <resultMap id="employeeResult" type="employee">
        <!--如果列名和类中的属性名相同,id,result可以省略的-->

        <!-- 分步查询,dept的封装策略,前提条件,在对应的对象的mapper接口中,恰好有要查询的数据 -->
        <association property="dept" javaType="dept" select="com.jsoft.dao.DeptMapper.findDeptById" column="did"></association>
    </resultMap>

    <!-- resultMap:自定义结果集映射   -->
    <select id="findEmployeeById" resultMap="employeeResult">
        select id,name,did from employee where id = #{id}
    </select>
    <select id="findDeptById" resultType="dept">
        select id,name from dept where id = #{id}
    </select>
    
public interface DeptMapper {

    Dept findDeptById(Integer id);

}

 

一对多查询

public interface DeptMapper {

    Dept findDeptById(Integer id);

    // 查询所有部门的同时带有员工信息
    List<Dept> findAllDepts();

}

 DeptMapper.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.jsoft.dao.DeptMapper">

    <select id="findDeptById" resultType="dept">
        select id,name from dept where id = #{id}
    </select>

<!--    <resultMap id="deptEmpResult" type="dept">-->
<!--        <id property="id" column="did"></id>-->
<!--        <result property="name" column="dname"></result>-->
<!--        <collection property="employees" ofType="employee">-->
<!--            <id column="eid" property="id"></id>-->
<!--            <result column="ename" property="name"></result>-->
<!--        </collection>-->
<!--    </resultMap>-->

<!--    <select id="findAllDepts" resultMap="deptEmpResult">-->
<!--        SELECT-->
<!--            d.id did,-->
<!--            d.NAME dname,-->
<!--            e.id eid,-->
<!--            e.NAME ename-->
<!--        FROM-->
<!--            dept d-->
<!--        INNER JOIN employee e ON d.id = e.did-->
<!--    </select>-->
    <resultMap id="deptEmpResult" type="dept">
        <id property="id" column="did"></id>
        <result property="name" column="dname"></result>
        <collection property="employees" ofType="employee" select="com.jsoft.dao.EmployeeMapper.getEmpByDid" column="id"></collection>
    </resultMap>

    <select id="findAllDepts" resultMap="deptEmpResult">
       select id,name from dept
    </select>


</mapper>

测试类

import com.jsoft.dao.DeptMapper;
import com.jsoft.dao.EmployeeMapper;
import com.jsoft.entity.Dept;
import com.jsoft.entity.Employee;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;
public class DeptMapperTest {

    SqlSession session;

    @Before
    public void before() {
        InputStream inputStream = UserMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        session = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        try {
            session.commit();
            session.close();
        }catch(Exception e){
            e.printStackTrace();
            session.rollback();
        }
    }

    @Test
    public void testFindAllDepts() {

        DeptMapper mapper = session.getMapper(DeptMapper.class);
        List<Dept> depts = mapper.findAllDepts();
        System.out.println(depts);
    }


}

 

MyBatis缓存

一级缓存默认开启,缓存范围Sq|Session会话

一级缓存:sqlSession级别的缓存
       执行流程:第一次发起查询sql查询用户id为3的用户,先去缓存中查看是否有id为3的用户,如果没有,再去数据库中查询,
               如果查到了,则把这条记录放进缓存中。
               如果session执行了插入,更新,删除操作,以及缓存会被清空
               mybatis是默认开启一级缓存
   一级缓存失效的情况:
               1、sqlSession不同
               2、当sqlSession对象相同,查询的数据不同。
               3、当sqlSession对象相同,两次查询之间进行插入,修改,删除的操作
               4、当sqlSession对象相同,两次查询之间手动清除了一级缓存

 二级缓存:mapper级别的缓存,接口级别的。
       多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,所得到的数据会存在二级缓存中
       二级缓存是跨sqlSession的
       二级缓存相比一级缓存范围更大。多个sqlSession可以共享一个二级缓存
       当session关闭时,会把数据提交到二级缓存
       查询是,从二级缓存中获取到的是数据的镜像,并不是真实的数据

 延迟加载(懒加载):什么时候用,什么时候再去加载,不用的时候,不加载。

二级缓存手动开启,属于范围Mapper Namespace

缓存的范围

 

 二级缓存的使用

二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,MyBatis要求返回的POJO必须是可

序列化的 。开启二级缓存的条件也是比较简单,通过直接在 MyBatis 配置文件中通过

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

来开启二级缓存,还需要在 Mapper 的xml 配置文件中加入 <cache> 标签

缓存标签的配置

设置 cache 标签的属性

cache 标签有多个属性,一起来看一些这些属性分别代表什么意义

eviction: 缓存回收策略,有这几种回收策略

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

默认是 LRU 最近最少回收策略

  •  flushinterval 缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一

个毫秒值

  •  readOnly : 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作

都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓

存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修

  •  size : 缓存存放多少个元素
  •  type : 指定自定义缓存的全类名(实现Cache 接口即可)
  •  blocking : 若缓存中找不到对应的key,是否会一直blocking,直到有对应的

数据进入缓存。

二级缓存运行规则

二级开启后默认所有查询操作均使用缓存

写操作commit提交时对该namespace缓存强制清空

配置useCache=false可以不用缓存

配置flushCache=true代表强制清空缓存

 

posted @ 2022-09-17 19:10  一只神秘的猫  阅读(110)  评论(0)    收藏  举报