Myabtis再学习-这一篇就够了

文章目录

一、从JDBC谈起

提示:这里创建的工程是maven工程

1.1创建工程

在这里插入图片描述

1.2pom.xml依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.32</version>
</dependency>

1.3数据库信息

DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user (
id char(32) NOT NULL,
user_name varchar(32) DEFAULT NULL,
password varchar(32) DEFAULT NULL,
name varchar(32) DEFAULT NULL,
age int(10) DEFAULT NULL,
sex int(2) DEFAULT NULL,
birthday date DEFAULT NULL,
created datetime DEFAULT NULL,
updated datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1.4插入数据

INSERT INTO ssmdemo.tb_user ( user_name, password, name, age, sex, birthday, created, updated) VALUES ( 'luoke', '123456', '雒珂', '22', '1', '1990-09-02', sysdate(), sysdate());
INSERT INTO ssmdemo.tb_user ( user_name, password, name, age, sex, birthday, created, updated) VALUES ( 'qly', '123456', '秦罗亚', '22', '1', '1993-09-05', sysdate(), sysdate());

1.5JDBC代码回顾

public class JdbcTest {
    public static void main(String[] args) throws Exception {
        Connection connection = null;
        PreparedStatement prepareStatement = null;
        ResultSet rs = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取连接
            String url = "jdbc:mysql://127.0.0.1:3306/ssmdemo";
            String user = "root";
            String password = "root";
            connection = DriverManager.getConnection(url, user, password);
            // 获取statement,preparedStatement
            String sql = "select * from tb_user where id=?";
            prepareStatement = connection.prepareStatement(sql);
            // 设置参数
            prepareStatement.setLong(1, 1l);
            // 执行查询
            rs = prepareStatement.executeQuery();
            // 处理结果集
            while (rs.next()) {
                System.out.println(rs.getString("user_name"));
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
                System.out.println(rs.getDate("birthday"));
            }
        } finally {
            // 关闭连接,释放资源
            if (rs != null) {
                rs.close();
            }
            if (prepareStatement != null) {
                prepareStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }
}

1.6JDBC缺点

在这里插入图片描述



二、Mybatis介绍(摘官网)

2.1官网简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

2.1Mybaits整体架构

在这里插入图片描述
在这里插入图片描述

三、快速入门(quick start)

3.1引入依赖(pom.xml)

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

3.2全局配置文件(mybatis-config.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>
<properties>
	<property name="driver" value="com.mysql.jdbc.Driver"/>
	<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true"/>
	<property name="username" value="root"/>
    	<property name="password" value="123456"/>
   </properties>

   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110" />
            <property name="username" value="root" />
            <property name="password" value="root" />
         </dataSource>
      </environment>
      <environment id="development">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
            <property name="url" value="${url}" />
            <property name="username" value="${username}" />
            <property name="password" value="${password}" />
         </dataSource>
      </environment>
   </environments>
  </configuration>

3.3配置Map.xml(MyMapper.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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="MyMapper">
   <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
      resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
    -->
   <select id="selectUser" resultType="com.zpc.mybatis.User">
      select * from tb_user where id = #{id}
   </select>
</mapper>

3.4修改全局配置文件(mybatis-config.xml)

<configuration>
   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" />
            <property name="username" value="root" />
            <property name="password" value="root" />
         </dataSource>
      </environment>
   </environments>
   <mappers>
     <mapper resource="mappers/MyMapper.xml" />
   </mappers>
</configuration>

3.5构建sqlSessionFactory(MybatisTest.java)

	// 指定全局配置文件
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

3.6.打开sqlSession会话,并执行sql(MybatisTest.java)

	 // 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
        // 第二个参数:指定传入sql的参数:这里是用户id
        User user = sqlSession.selectOne("MyMapper.selectUser", 1);
        System.out.println(user);

完整代码

MybatisTest.java

public class MybatisTest {
   public static void main(String[] args) throws Exception {
      // 指定全局配置文件
      String resource = "mybatis-config.xml";
      // 读取配置文件
      InputStream inputStream = Resources.getResourceAsStream(resource);
      // 构建sqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      // 获取sqlSession
      SqlSession sqlSession = sqlSessionFactory.openSession();
      try {
         // 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
         // 第二个参数:指定传入sql的参数:这里是用户id
         User user = sqlSession.selectOne("MyMapper.selectUser", 1);
         System.out.println(user);
      } finally {
         sqlSession.close();
      }
   }
}

User.java

public class User {//省略getter和setter
    private String id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private Integer sex;
    private Date birthday;
    private String created;
    private String updated;
    }

目录结构如下

在这里插入图片描述


四、分析

4.1.引入日志依赖包(pom.xml)

会自动引入log4j以及slf4j-api

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.5</version>
</dependency>

4.2.添加log4j.properties

log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

再次运行程序会打印日志:

2022-11-04 12:01:52,162 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2022-11-04 12:01:52,384 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1967892594.
2022-11-04 12:01:52,384 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@754ba872]
2022-11-04 12:01:52,386 [main] [MyMapper.selectUser]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2022-11-04 12:01:52,411 [main] [MyMapper.selectUser]-[DEBUG] ==> Parameters: 1(Integer)
2022-11-04 12:01:52,433 [main] [MyMapper.selectUser]-[DEBUG] <==      Total: 1
User{id='1', userName='null', password='123456', name='雒珂', age=22, sex=1, birthday='1990-09-02', created='2022-11-04 11:20:38.0', updated='2022-11-04 11:20:38.0'}
2022-11-04 12:01:52,433 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@754ba872]
2022-11-04 12:01:52,433 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@754ba872]
2022-11-04 12:01:52,434 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 1967892594 to pool.

4.3.MyBatis使用步骤总结

1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
2)创建SqlSessionFactory
3)通过SqlSessionFactory创建SqlSession对象
4)通过SqlSession操作数据库 CRUD
5)调用session.commit()提交事务
6)调用session.close()关闭会话

五、完整的CRUD操作

5.1.创建UserDao接口

public interface UserDao {

    /**
     * 根据id查询用户信息
     *
     * @param id
     * @return
     */
    public User queryUserById(String id);

    /**
     * 查询所有用户信息
     *
     * @return
     */
    public List<User> queryUserAll();

    /**
     * 新增用户
     *
     * @param user
     */
    public void insertUser(User user);

    /**
     * 更新用户信息
     *
     * @param user
     */
    public void updateUser(User user);

    /**
     * 根据id删除用户信息
     *
     * @param id
     */
    public void deleteUser(String id);
}

5.2创建UserDaoImpl

public class UserDaoImpl implements UserDao {
    private SqlSession sqlSession;

    public UserDaoImpl(SqlSession sqlSession){
        this.sqlSession = sqlSession;
    }

    public User queryUserById(String id) {
        return this.sqlSession.selectOne("queryUserById",id);
    }

    public List<User> queryUserAll() {
        return this.sqlSession.selectList("queryUserAll");
    }

    public void insertUser(User user) {
        this.sqlSession.insert("insertUser", user);
    }

    public void updateUser(User user) {
        this.sqlSession.update("updateUser", user);
    }

    public void deleteUser(String id) {
        this.sqlSession.delete("deleteUser", id);
    }
}

5.3编写UserDao对应的UserDaoMapper.xml(一个dao接口对应一个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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="com.luoke.dao.UserDao">
    <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
       resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
     -->
    <!--<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">-->
    <!--select * from tb_user where id = #{id}-->
    <!--</select>-->

    <!--使用别名-->
    <select id="queryUserById" resultType="com.luoke.pojo.User">
        select
            tuser.id as id,
            tuser.user_name as userName,
            tuser.password as password,
            tuser.name as name,
            tuser.age as age,
            tuser.birthday as birthday,
            tuser.sex as sex,
            tuser.created as created,
            tuser.updated as updated
        from
            tb_user tuser
        where tuser.id = #{id};
    </select>

    <select id="queryUserAll" resultType="com.luoke.pojo.User">
        select * from tb_user;
    </select>

    <!--插入数据-->
    <insert id="insertUser" parameterType="com.luoke.pojo.User">
        INSERT INTO tb_user (
            user_name,
            password,
            name,
            age,
            sex,
            birthday,
            created,
            updated
        )
        VALUES
        (
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            now(),
            now()
        );
    </insert>

    <update id="updateUser" parameterType="com.luoke.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>

    <delete id="deleteUser">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

在mybatis-config.xml中添加配置:

<mappers>
    <mapper resource="mappers/MyMapper.xml"/>
    <mapper resource="mappers/UserDaoMapper.xml"/>
</mappers>

5.4添加UserDao的测试用例

Pom文件中添加junit依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

按住Alt+Enter,选择create test

在这里插入图片描述
在这里插入图片描述

测试完成

在这里插入图片描述

5.6.目录结构

在这里插入图片描述

5.7解决数据库字段名和实体类属性名不一致的问题

原因:数据库的字段名是user_name,POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名。
在这里插入图片描述

解决方案1:在sql语句中使用别名:

<select id="queryUserById" resultType="com.luoke.pojo.User">
   select
    tuser.id as id,
    tuser.user_name as userName,
    tuser.password as password,
    tuser.name as name,
    tuser.age as age,
    tuser.birthday as birthday,
    tuser.sex as sex,
    tuser.created as created,
    tuser.updated as updated
    from
    tb_user tuser
    where tuser.id = #{id};
</select>

解决方案2: 参考后面的resultMap –mapper具体的配置的时候

解决方案3:参考驼峰匹配 — mybatis-config.xml 的时候

六、动态代理Mapper实现类

6.1.思考上述CRUD中的问题

1、接口->实现类->mapper.xml
2、实现类中,使用mybatis的方式非常类似
3、xml中的sql statement 硬编码到java代码中。
思考:能否只写接口,不写实现类。只编写接口和Mapper.xml即可?

因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。因此mybatis提供了接口的动态代理。

6.2.使用动态代理改造CRUD

修改测试用例的setUp方法
在这里插入图片描述
执行queryUserAll()方法
在这里插入图片描述
如果在Usermapper.xml中的namespace没有配置全路径会报错
如果希望使用mybatis通过的动态代理的接口,就需要namespace中的值,和需要对应的Mapper(dao)接口的全路径一致。Mapper中Namespace的定义本身是没有限制的,只要不重复即可,但如果使用Mybatis的DAO接口动态代理,则namespace必须为DAO接口的全路径,例如:com.luoke.dao.UserDao

6.3完整的例子

1、创建UserMapper接口(对应原UserDao)

public interface UserMapper {
   
   /**
    * 登录(直接使用注解指定传入参数名称)
    * @param userName
    * @param password
    * @return
    */
   public User login(@Param("userName") String userName, @Param("password") String password);
   
   /**
    * 根据表名查询用户信息(直接使用注解指定传入参数名称)
    * @param tableName
    * @return
    */
   public List<User> queryUserByTableName(@Param("tableName") String tableName);
   
   /**
    * 根据Id查询用户信息
    * @param id
    * @return
    */
   public User queryUserById(Long id);
   
   /**
    * 查询所有用户信息
    * @return
    */
   public List<User> queryUserAll();
   
   /**
    * 新增用户信息
    * @param user
    */
   public void insertUser(User user);
   
   /**
    * 根据id更新用户信息
    * @param user
    */
   public void updateUser(User user);
   
   /**
    * 根据id删除用户信息
    * @param id
    */
   public void deleteUserById(Long id);
}

2、创建UserMapper.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:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.luoke.dao.UserMapper">
    <!--
       1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
       2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
     -->
    <select id="queryUserByTableName" resultType="com.luoke.pojo.User">
        select * from ${tableName}
    </select>

    <select id="login" resultType="com.luoke.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>

    <!-- statement,内容:sql语句。
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <select id="queryUserById" resultType="com.luoke.pojo.User">
        select * from tb_user where id = #{id}
    </select>
    <select id="queryUserAll" resultType="com.luoke.pojo.User">
        select * from tb_user
    </select>
    <!-- 新增的Statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
       useGeneratedKeys:开启主键回写
       keyColumn:指定数据库的主键
       keyProperty:主键对应的pojo属性名
     -->
    <insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
            parameterType="com.luoke.pojo.User">
        INSERT INTO tb_user (
            id,
            user_name,
            password,
            name,
            age,
            sex,
            birthday,
            created,
            updated
        )
        VALUES
        (
            null,
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            NOW(),
            NOW()
        );
    </insert>
    <!-- 
       更新的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <update id="updateUser" parameterType="com.luoke.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>
    <!-- 
       删除的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <delete id="deleteUserById" parameterType="java.lang.String">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

3、全局配置文件mybatis-config.xml引入UserMapper.xml

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

4、创建UserMapper测试用例(alt+insert)

public class UserMapperTest {
    public UserMapper userMapper;

    @Before
    public void setUp() throws Exception {
        // 指定配置文件
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径
        // 2. 映射文件的statement的id必须和mapper接口的方法名保持一致
        // 3. Statement的resultType必须和mapper接口方法的返回类型一致
        // 4. statement的parameterType必须和mapper接口方法的参数类型一致(不一定)
        this.userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void testQueryUserByTableName() {
        List<User> userList = this.userMapper.queryUserByTableName("tb_user");
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testLogin() {
        System.out.println(this.userMapper.login("hj", "123456"));
    }

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById(1L));
    }

    @Test
    public void testQueryUserAll() {
        List<User> userList = this.userMapper.queryUserAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setAge(20);
        user.setBirthday(new Date());
        user.setName("大神");
        user.setPassword("123456");
        user.setSex(2);
        user.setUserName("bigGod222");
        this.userMapper.insertUser(user);
        System.out.println(user.getId());
    }

    @Test
    public void testUpdateUser() {
        User user = new User();
        user.setBirthday(new Date());
        user.setName("静静");
        user.setPassword("123456");
        user.setSex(0);
        user.setUserName("Jinjin");
        user.setId("1");
        this.userMapper.updateUser(user);
    }

    @Test
    public void testDeleteUserById() {
        this.userMapper.deleteUserById("1");
    }
}

目录结构

在这里插入图片描述

6.4动态代理总结

在这里插入图片描述

七.mybatis-config.xml详解

mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序在这里插入图片描述

7.1.properties属性读取外部资源

properties配置的属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

然后其中的属性就可以在整个配置文件中被用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。

属性也可以被传递到 SqlSessionFactoryBuilder.build()方法中。例如:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
// ... or ...
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

1)在 properties 元素体内指定的属性首先被读取。
2)然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
3)最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。

7.2.settings设置

在这里插入图片描述

mapUnderscoreToCamelCase:开启驼峰匹配:完成经典的数据库命名到java属性的映射。
经典数据库命名:如果多个单词之间,通常使用下划线进行连接。
驼峰匹配:相当于去掉数据中名字的下划线,和java进行匹配

例如:数据库中user_name---->userName

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

注意settings是有顺序的,不然会报错
在这里插入图片描述

测试:
没有开启驼峰匹配:(userName=‘null’)

2018-07-01 13:57:56,486 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 13:57:56,524 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:57:56,568 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='null', password='123456', name='大神', age=20, sex=2, birthday='2018-07-01', created='2022-11-01 13:36:09.0', updated='2022-11-01 13:36:09.0'}

开启驼峰匹配:(userName有值)

2018-07-01 13:58:40,599 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 13:58:40,642 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:58:40,661 [main] [com.luoke.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='大神', age=20, sex=2, birthday='2022-11-01', created='2022-11-01 13:36:09.0', updated='2022-11-01 13:36:09.0'}

7.3.typeAliases

类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

<typeAliases>
    <typeAlias type="com.luoke.pojo.User" alias="User"/>
</typeAliases>

之后的namespace中只需要些User即可,不需要配置全路径
缺点:每个pojo类都要去配置。
解决方案:使用扫描包,扫描指定包下的所有类,扫描之后的别名就是类名(不区分大小写),建议使用的时候和类名一致。

Mybatis已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的
比如java.lang.Integer
java.lang.String

7.4.typeHandlers(类型处理器)

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。

7.5.plugins(插件)拦截器

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

现在一些MyBatis 插件比如PageHelper都是基于这个原理,有时为了监控sql执行效率,也可以使用插件机制
原理:在这里插入图片描述

自定义拦截器:

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

mybatis-config.xml中添加配置:

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。

7.6.environments(环境)

MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。

7.7.mappers

需要告诉 MyBatis 到哪里去找到 SQL 映射语句。即告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper。类名,也叫做mapper

1、定义一个接口。
2、在接口所在的包中定义mapper.xml,并且要求xml文件和interface的名称要相同。
3、在mybatis-config.xml 中通过class路径,引入mapper(注解方式)。要求mapper.xml 中的名称空间是类的接口的全路径。

注解方式:

<mappers>
    <mapper resource="mappers/MyMapper.xml"/>
    <mapper resource="mappers/UserDaoMapper.xml"/>
    <!--注解方式可以使用如下配置方式-->
    <mapper class="com.luoke.dao.UserMapper"/>
</mappers>

问题:

1、mapper.xml 和 java文件没有分离。 之后的教程讲述和spring整合之后解决。
2、需要一个一个的去加载mapper。
当然也可以使用包扫描(必须使用注解方式,即在接口方法上使用注解,如@Select("select * from tb_user ")):

缺点:
1、如果包的路径有很多?
2、mapper.xml和mapper.java没有分离。
spring整合的时候解决。

八、Mapper XML文件详解

8.1.CRUD标签

8.1.1.select

select – 书写查询sql语句
select中的几个属性说明:
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)
parameterType:传入参数类型。可以省略

8.1.2.insert

insert 的几个属性说明:
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
标签内部:具体的sql语句。

8.1.3.update

id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。

8.1.4.delete

delete 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。

8.2.#{}和${}

场景:数据库有两个一模一样的表。历史表,当前表
查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。
希望使用1个方法来完成操作。

<select id="queryUserByTableName" resultType="com.luoke.pojo.User">
    select * from #{tableName}
</select>

/**
 * 根据表名查询用户信息(直接使用注解指定传入参数名称)
 *
 * @param tableName
 * @return
 */
public List<User> queryUserByTableName(String tableName);

有问题,报语法错误:相当于执行了这样一条sql:
select * from “tb_user”;
显然表名多了引号。

改正:

<select id="queryUserByTableName" resultType="com.luoke.pojo.User">
    select * from ${tableName}
</select>

注意:
#{} 只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。

美元${} 是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。

使用${} 去取出参数值信息,需要使用${value}
#{} 只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。

推荐:

/**
 * 根据表名查询用户信息(直接使用注解指定传入参数名称)
 *
 * @param tableName
 * @return
 */
public List<User> queryUserByTableName(@Param("tableName") String tableName);

<select id="queryUserByTableName" resultType="com.luoke.pojo.User">
    select * from ${tableName}
</select>

#{}多个参数时:

/**
 * 登录(直接使用注解指定传入参数名称)
 *
 * @param userName
 * @param password
 * @return
 */
public User login( String userName, String password);

<select id="login" resultType="com.luoke.pojo.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

报错

org.apache.ibatis.exceptions.PersistenceException: 
Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]

解决方案一:

org.apache.ibatis.exceptions.PersistenceException: 
Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]

解决方案二:

<select id="login" resultType="com.luoke.pojo.User">
    select * from tb_user where user_name = #{param1} and password = #{param2}
</select>

最终解决方案:

/**
 * 登录(直接使用注解指定传入参数名称)
 *
 * @param userName
 * @param password
 * @return
 */
public User login(@Param("userName") String userName, @Param("password") String password);

<select id="login" resultType="com.luoke.pojo.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过${“xxxx”}或#{“xxxx”}
sql语句动态生成的时候,使用${};
sql语句中某个参数进行占位的时候#{}

8.3面试题(#、$区别)

/**
 * #号
 * @param username1
 * @return
 */
User queryUserListByName1(@Param("username1") String username1);

/**
 * $号
 * @param username2
 * @return
 */
User queryUserListByName2(@Param("username2") String username2);

<select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name=#{username1}
</select>

<select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>

8.4.resultMap

在这里插入图片描述
在这里插入图片描述
使用
在这里插入图片描述

8.5 Sql片段

<sql id=””></sql>
<include refId=”” />

例如在UserMapper.xml中定义如下片段:

<sql id="commonSql">
		id,
			user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated	
</sql> 

则可以在UserMapper.xml中使用它:

<select id="queryUserById" resultMap="userResultMap">
	select <include refid="commonSql"></include> from tb_user where id = #{id}
</select>

<select id="queryUsersLikeUserName" resultType="User">
	select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>

Sql片段也可以定义在单独的.xml文件中如:
定义CommonSQL.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="CommonSQL">
	<sql id="commonSql">
		id,
			user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated	
	</sql>
</mapper>

使用:

	<select id="queryUserById" resultMap="userResultMap">
		select <include refid="CommonSQL.commonSql"></include> from tb_user where id = #{id}
	</select>
	
	<select id="queryUsersLikeUserName" resultType="User">
		select <include refid="CommonSQL.commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
	</select>

当然要完成这个功能还需要在全局配置文件mybatis-config.xml中引入该外部配置文件:

<mappers>
		<mapper resource="CommonSQL.xml"/>
		<!-- 开启mapper接口的包扫描,基于class的配置方式 -->
		<package name="com.zpc.mybatis.mapper"/>
</mappers>

九、动态sql

在这里插入图片描述

9.1. if

场景:查询男性用户,如果输入了姓名,按姓名模糊查询

定义接口:

/**
 * 查询男性用户,如果输入了姓名,则按姓名查询
 * @param name
 * @return
 */
List<User> queryUserList(@Param("name") String name);

编写mapper

<select id="queryUserList" resultType="User">
    select * from tb_user WHERE sex=1
    <if test="name!=null and name.trim()!=''">
      and name like '%${name}%'
    </if>
</select>

测试

@Test
public void testqueryUserList() {
    List<User> users = this.userMapper.queryUserList(null);
    for (User user : users) {
        System.out.println(user);
    }
}

9.2.choose when otherwise

场景:查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。

定义接口:

/**
 * 查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。
 * @param name
 * @param age
 * @return
 */
List<User> queryUserListByNameOrAge(@Param("name") String name,@Param("age") Integer age);

编写mapper配置:

<select id="queryUserListByNameOrAge" resultType="com.luoke.pojo.User">
    select * from tb_user WHERE sex=1
    <!--
    1.一旦有条件成立的when,后续的when则不会执行
    2.当所有的when都不执行时,才会执行otherwise
    -->
    <choose>
        <when test="name!=null and name.trim()!=''">
            and name like '%${name}%'
        </when>
        <when test="age!=null">
            and age = #{age}
        </when>
        <otherwise>
            and name='鹏程'
        </otherwise>
    </choose>
</select>

测试:

@Test
public void queryUserListByNameOrAge() throws Exception {
    List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
    for (User user : users) {
        System.out.println(user);
    }
}

9.3.where 和set

场景一:查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。

接口:

/**
 * 查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立
 * @param name
 * @param age
 * @return
 */
List<User> queryUserListByNameAndAge(@Param("name") String name,@Param("age") Integer age);

配置:

<select id="queryUserListByNameAndAge" resultType="User">
    select * from tb_user
    <!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
    <where>
        <if test="name!=null and name.trim()!=''">
            and name like '%${name}%'
        </if>
        <if test="age!=null">
            and age = #{age}
        </if>
    </where>
</select>

测试:

@Test
public void queryUserListByNameAndAge() throws Exception {
    List<User> users = this.userMapper.queryUserListByNameAndAge("鹏程", 20);
    for (User user : users) {
        System.out.println(user);
    }
}

场景二:修改用户信息,如果参数user中的某个属性为null,则不修改。
接口:

/**
 * 根据id更新用户信息
 *
 * @param user
 */
public void updateUser(User user);

配置:

<update id="updateUser" parameterType="User">
    UPDATE tb_user
    <trim prefix="set" suffixOverrides=",">
        <if test="userName!=null">user_name = #{userName},</if>
        <if test="password!=null">password = #{password},</if>
        <if test="name!=null">name = #{name},</if>
        <if test="age!=null">age = #{age},</if>
        <if test="sex!=null">sex = #{sex},</if>
        <if test="birthday!=null">birthday = #{birthday},</if>
        updated = now(),
    </trim>
    WHERE
    (id = #{id});
</update>

测试:

@Test
public void testUpdateUser() {
    User user = new User();
    user.setBirthday(new Date());
    user.setName("静静");
    user.setPassword("123456");
    user.setSex(0);
    user.setUserName("Jinjin");
    user.setId("1");
    this.userMapper.updateUser(user);
}

9.4.foreach

场景:按照多个id查询用户信息

接口:

/**
 * 按多个Id查询
 * @param ids
 * @return
 */
List<User> queryUserListByIds(@Param("ids") String[] ids);

测试:

@Test
public void queryUserListByIds() throws Exception {
    List<User> users = this.userMapper.queryUserListByIds(new String[]{"1","2"});
    for (User user : users) {
        System.out.println(user);
    }
}

If:testognl表达式或者简单java代码
Choose when otherwise—>相当于if else if else
When test参考if
Where set 都有一定的纠错功能
Trim:prefix suffix prefixOverrides suffixOverrides
Foreach:collection item saparator open close

十、缓存

10.1.一级缓存

在这里插入图片描述

在mybatis中,一级缓存默认是开启的,并且一直无法关闭

一级缓存满足条件:
1、同一个session中
2、相同的SQL和参数

测试:

@Test
public void testQueryUserById() {
    System.out.println(this.userMapper.queryUserById("1"));
    System.out.println(this.userMapper.queryUserById("1"));
}
2018-07-01 17:08:50,156 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:08:50,421 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:08:50,423 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:08:50,476 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:08:50,509 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}

使用:sqlSession.clearCache();可以强制清除缓存

测试:

@Test
public void testQueryUserById() {
    System.out.println(this.userMapper.queryUserById("1"));
    sqlSession.clearCache();
    System.out.println(this.userMapper.queryUserById("1"));
}

日志:

2018-07-01 17:10:51,065 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:10:51,359 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:10:51,360 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:10:51,408 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:10:51,437 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:10:51,440 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}

执行update、insert、delete的时候,会清空缓存
测试:

@Test
public void testQueryUserById() {
    System.out.println(this.userMapper.queryUserById("1"));
    //sqlSession.clearCache();

    User user=new User();
    user.setName("美女");
    user.setId("1");
    userMapper.updateUser(user);

    System.out.println(this.userMapper.queryUserById("1"));
}

日志:

2018-07-01 17:18:15,128 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:18:15,399 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:18:15,401 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:18:15,466 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:18:15,492 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
2018-07-01 17:18:15,527 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==>  Preparing: UPDATE tb_user set name = ?, updated = now() WHERE (id = ?); 
2018-07-01 17:18:15,529 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Parameters: 美女(String), 1(String)
2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] <==    Updates: 1
2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:18:15,533 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:18:15,538 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}

10.2.二级缓存

mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

开启二级缓存:

<mapper namespace="com.luoke.dao.UserMapper">
    <cache/>
</mapper>

测试:

@Test
public void testCache() {
    System.out.println(this.userMapper.queryUserById("1"));

    sqlSession.close();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    System.out.println(mapper.queryUserById("1"));
}

开启二级缓存,必须序列化:

public class User implements Serializable{
    private static final long serialVersionUID = -3330851033429007657L;

日志:

2018-07-01 17:23:39,335 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:23:39,664 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2092769598.
2018-07-01 17:23:39,665 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
2018-07-01 17:23:39,712 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:23:39,734 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
2018-07-01 17:23:39,743 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cbd213e]
2018-07-01 17:23:39,744 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2092769598 to pool.
2018-07-01 17:23:39,746 [main] [com.zpc.mybatis.dao.UserMapper]-[DEBUG] Cache Hit Ratio [com.zpc.mybatis.dao.UserMapper]: 0.5
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}

关闭二级缓存:
不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
在这里插入图片描述

<settings>
    <!--开启驼峰匹配-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!--开启二级缓存,全局总开关,这里关闭,mapper中开启了也没用-->
    <setting name="cacheEnabled" value="false"/>
</settings>

在这里插入图片描述

十一、高级查询

11.1.表关系说明

在这里插入图片描述
创建order表:

CREATE TABLE tb_order (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) DEFAULT NULL,
order_number varchar(255) DEFAULT NULL,
create datetime DEFAULT NULL,
updated datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
}

需求说明:在这里插入图片描述
12.2.一对一查询
方法一:核心思想扩展Order对象,来完成映射
新建OrderUser实体类继承Order:

public class OrderUser extends Order {
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private Integer sex;
    private Date birthday;
    private Date created;
    private Date updated;
}

OrderMapper接口:

public interface OrderMapper {
     OrderUser queryOrderUserByOrderNumber(@Param("number") String number);
}

配置OrderMapper:

 <mapper namespace="com.luoke.dao.OrderMapper">
    <select id="queryOrderUserByOrderNumber" resultType="com.zpc.mybatis.pojo.OrderUser">
      select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
   </select>
</mapper>

测试:

@Test
public void queryOrderUserByOrderNumber() throws Exception {
    OrderUser orderUser = orderMapper.queryOrderUserByOrderNumber("201807010001");
    System.out.println(orderUser);
}

方法二:面向对象的思想,在Order对象中添加User对象。

在Order对象中添加User属性:

public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
    private User user;
}

接口:

/**
 * 根据订单号查询订单用户的信息
 * @param number
 * @return
 */
Order queryOrderWithUserByOrderNumber(@Param("number") String number);

使用resultType不能完成自动映射,需要手动完成结果集映射resultMap:

 <resultMap id="OrderUserResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
     <id column="id" property="id"/>
     <!--association:完成子对象的映射-->
     <!--property:子对象在父对象中的属性名-->
     <!--javaType:子对象的java类型-->
     <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
     <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
         <id column="user_id" property="id"/>
     </association>
 </resultMap>

 <select id="queryOrderWithUserByOrderNumber" resultMap="OrderUserResultMap">
   select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserByOrderNumber("201807010001");
    System.out.println(order.getUser());
}

11.3.一对多查询

一对多查询:查询订单,查询出下单人信息并且查询出订单详情。

Order类:

public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
    private User user;
    private List<OrderDetail> detailList;
}

public class OrderDetail {
    private Integer id;
    private Integer orderId;
    private Double totalPrice;
    private Integer status;
}

接口:

/**
 * 根据订单号查询订单用户的信息及订单详情
 * @param number
 * @return
 */
Order queryOrderWithUserAndDetailByOrderNumber(@Param("number") String number);

Mapper映射:

<resultMap id="OrderUserDetailResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
    <id column="id" property="id"/>
    <!--collection:定义子对象集合映射-->
    <!--association:完成子对象的映射-->
    <!--property:子对象在父对象中的属性名-->
    <!--javaType:子对象的java类型-->
    <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
    <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
        <id column="user_id" property="id"/>
    </association>
    <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">
        <id column="id" property="id"/>
    </collection>
</resultMap>

 <select id="queryOrderWithUserAndDetailByOrderNumber" resultMap="OrderUserDetailResultMap">
   select * from tb_order o
   left join tb_user u on o.user_id=u.id
   left join tb_orderdetail od on o.id=od.order_id
   where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserAndDetailByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserAndDetailByOrderNumber("201807010001");
    System.out.println(order.getUser());
    System.out.println(order.getDetailList());
}

11.4.多对多查询

多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。

OrderDetail类

public class OrderDetail {
    private Integer id;
    private Integer orderId;
    private Double totalPrice;
    private Integer status;
    private Item item;
}

public class Item {
    private Integer id;
    private String itemName;
    private Float itemPrice;
    private String itemDetail;
}

接口:

/**
 * 根据订单号查询订单用户的信息及订单详情及订单详情对应的商品信息
 * @param number
 * @return
 */
Order queryOrderWithUserAndDetailItemByOrderNumber(@Param("number") String number);

Mapper配置:

<resultMap id="OrderUserDetailItemResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
    <id column="id" property="id"/>
    <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
        <id column="user_id" property="id"/>
    </association>
    <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">
        <id column="detail_id" property="id"/>
        <association property="item" javaType="com.zpc.mybatis.pojo.Item" autoMapping="true">
            <id column="item_id" property="id"/>
        </association>
    </collection>
</resultMap>

 <select id="queryOrderWithUserAndDetailItemByOrderNumber" resultMap="OrderUserDetailItemResultMap">
   select * ,od.id as detail_id from tb_order o
   left join tb_user u on o.user_id=u.id
   left join tb_orderdetail od on o.id=od.order_id
   left join tb_item i on od.item_id=i.id
   where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserAndDetailItemByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserAndDetailItemByOrderNumber("201807010001");
    System.out.println(order);
    System.out.println(order.getUser());
    System.out.println(order.getDetailList());
}

至此,目录结构如下:

在这里插入图片描述
数据库脚本:

CREATE TABLE tb_order (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) DEFAULT NULL,
order_number varchar(255) DEFAULT NULL,
create datetime DEFAULT NULL,
updated datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO tb_order VALUES ('1', '2', '201807010001', '2018-07-01 19:38:35', '2018-07-01 19:38:40');

CREATE TABLE tb_item (
id int(11) NOT NULL,
itemName varchar(255) DEFAULT NULL,
itemPrice decimal(10,2) DEFAULT NULL,
itemDetail varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO tb_item VALUES ('1', '袜子', '29.90', '香香的自助');
INSERT INTO tb_item VALUES ('2', '套子', '99.99', '冈本001');

CREATE TABLE tb_orderdetail (
id int(11) NOT NULL AUTO_INCREMENT,
order_id int(11) DEFAULT NULL,
total_price decimal(10,0) DEFAULT NULL,
item_id int(11) DEFAULT NULL,
status int(10) unsigned zerofill DEFAULT NULL COMMENT '0成功非0失败',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO tb_orderdetail VALUES ('1', '1', '10000', '1', '0000000001');
INSERT INTO tb_orderdetail VALUES ('2', '1', '2000', '2', '0000000000');

11.5.resultMap的继承

在这里插入图片描述

11.6.高级查询的整理

resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
type 结果集对应的数据类型 id 唯一标识,被引用的时候,进行指定

<resultMap type="Order" id="orderUserLazyResultMap">
<!—定义pojo中的单个对象的 property 定义对象的属性名, javaType 属性的类型,
		<association property="user" javaType="User" autoMapping="true">
			<id />
		</association>
<!—如果属性是集合使用collection ,javaType 集合的类型,ofType 表示集中的存储的元素类型
		<collection property="details" javaType="List" ofType="OrderDetail" autoMapping="true">
			<id />
</resultMap>

十三、延迟加载在这里插入图片描述

在这里插入图片描述
编写接口:

在这里插入图片描述

Mapper配置:
在这里插入图片描述
测试:
在这里插入图片描述

结果:
在这里插入图片描述

开启延迟加载:
在这里插入图片描述
修改测试用例:
在这里插入图片描述

报错:在这里插入图片描述
添加cglib:

<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>3.1</version>
</dependency>

再次执行:

在这里插入图片描述

十三、如果sql语句中出现’<’的解决方案

13.1、使用xml中的字符实体

在这里插入图片描述
因为业务,需要在mybatis中,使用到大于号,小于号,所以就在SQL中直接使用了。
SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE
可是,在执行时,总报错误:

元素内容必须由格式正确的字符数据或标记组成。
把AND start_date >= CURRENT_DATE AND end_date <= CURRENT_DATE去掉,就没有问题,所以确定是因为大于号,小于号引起的问题。

于是就想到了特殊符号,于是用了转义字符把>和<替换掉,然后就没有问题了。
SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE

案例:

1.<if test="startDateTime!=null"> and mm.ttime &gt; to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss')</if>  
2.<if test="endDateTime!=null"> and mm.ttime &lt;= to_date(#{endDateTime},'yyyy-mm-dd hh24:mi:ss')</if>  

13.2、使用<![CDATA[ < ]]>

案例1:

1.<![CDATA[ 
2.       and mm.ttime > to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss') 
3.       and mm.ttime <= to_date(#{endDateTime},'yyyy-mm-dd hh24:mi:ss') 
4.]]>  

案例2:

mapper文件示例代码 :

and (t1.status <![CDATA[ >= ]]> 1  and  t1.status <![CDATA[ <= ]]> 2)
上述代码其实对应的sql:
and (t1.status > =1 andt1.status <= 2)

注意:

使用标记的sql语句中的 等标签不会被解析。

十四.Spring 集成Mybatis

14.1引入spring和Mybatis相关依赖

pom.xml

<!--数据库连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.8</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.1.3.RELEASE</version>
</dependency>
<!--spring集成Junit测试-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.1.3.RELEASE</version>
    <scope>test</scope>
</dependency>
<!--spring容器-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.3.RELEASE</version>
</dependency>

14.2配置spring配置文件

applicationContext-dao.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:properties/*.properties"/>
    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url"
                  value="jdbc:mysql://${jdbc.host}:3306/${jdbc.database}?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull"/>
        <property name="username" value="${jdbc.userName}"/>
        <property name="password" value="${jdbc.passWord}"/>
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="${jdbc.initialSize}"></property>
        <!-- 连接池最大数据库连接数  0 为没有限制 -->
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <!-- 连接池最大的空闲连接数,这里取值为20,表示即使没有数据库连接时依然可以保持20空闲的连接,而不被清除,随时处于待命状态 0 为没有限制 -->
        <property name="maxIdle" value="${jdbc.maxIdle}"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="${jdbc.minIdle}"></property>
        <!--最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制-->
        <property name="maxWait" value="${jdbc.maxWait}"></property>
    </bean>

    <!-- spring和MyBatis完美整合 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:mappers/*.xml"></property>
        <!--如果mybatis-config.xml没有特殊配置也可以不需要下面的配置-->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
    </bean>

    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zpc.mybatis.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

    <!-- (事务管理)transaction manager -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.host=localhost
jdbc.database=ssmdemo
jdbc.userName=root
jdbc.passWord=123456
jdbc.initialSize=0
jdbc.maxActive=20
jdbc.maxIdle=20
jdbc.minIdle=1
jdbc.maxWait=1000

由于applicationContext-dao.xml中配置了Mapper接口扫描,所以删除mybatis-config.xml中的配置,否则报已映射错误:
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for MyMapper.selectUser
删除mybatis-config.xml中的映射配置:

<!--<mappers>-->
    <!--<mapper resource="mappers/MyMapper.xml"/>-->
    <!--<mapper resource="mappers/UserDaoMapper.xml"/>-->
    <!--<mapper resource="mappers/UserMapper.xml"/>-->
    <!--<mapper resource="mappers/OrderMapper.xml"/>-->
<!--</mappers>-->

或者在构建sqlSessionFactory时不配置mybatis-config.xml也行:

<!-- spring和MyBatis完美整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperLocations" value="classpath:mappers/*.xml"></property>
    <!--如果mybatis-config.xml没有特殊配置也可以不需要下面的配置-->
    <!--<property name="configLocation" value="classpath:mybatis-config.xml" />-->
</bean>

14.3 测试

UserMapperSpringTest.java

//目标:测试一下spring的bean的某些功能
@RunWith(SpringJUnit4ClassRunner.class)//junit整合spring的测试//立马开启了spring的注解
@ContextConfiguration(locations="classpath:spring/applicationContext-*.xml")//加载核心配置文件,自动构建spring容器
public class UserMapperSpringTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testQueryUserByTableName() {
        List<User> userList = this.userMapper.queryUserByTableName("tb_user");
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testLogin() {
        System.out.println(this.userMapper.login("hj", "123456"));
    }

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        User user = new User();
        user.setName("美女");
        user.setId("1");
        userMapper.updateUser(user);

        System.out.println(this.userMapper.queryUserById("1"));
    }

    @Test
    public void testQueryUserAll() {
        List<User> userList = this.userMapper.queryUserAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setAge(20);
        user.setBirthday(new Date());
        user.setName("大神");
        user.setPassword("123456");
        user.setSex(2);
        user.setUserName("bigGod222");
        this.userMapper.insertUser(user);
        System.out.println(user.getId());
    }

    @Test
    public void testUpdateUser() {
        User user = new User();
        user.setBirthday(new Date());
        user.setName("静静");
        user.setPassword("123456");
        user.setSex(0);
        user.setUserName("Jinjin");
        user.setId("1");
        this.userMapper.updateUser(user);
    }

    @Test
    public void testDeleteUserById() {
        this.userMapper.deleteUserById("1");
    }

    @Test
    public void testqueryUserList() {
        List<User> users = this.userMapper.queryUserList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void queryUserListByNameAndAge() throws Exception {
        List<User> users = this.userMapper.queryUserListByNameAndAge("鹏程", 20);
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void queryUserListByNameOrAge() throws Exception {
        List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void queryUserListByIds() throws Exception {
        List<User> users = this.userMapper.queryUserListByIds(new String[]{"5", "2"});
        for (User user : users) {
            System.out.println(user);
        }
    }

目录结构:在这里插入图片描述

十五、SpringBoot 整合Mybatis

2.pom依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
    </dependencies>

15.1目录结构

在这里插入图片描述
3.application.yml文件

数据库连接信息

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/weixin?serverTimezone=GMT%2B8&useSSL=true
    username: root
    password: root

4.在application.properties文件中加入:

#端口号
server.port=8080
#druid数据库连接池
type=com.alibaba.druid.pool.DruidDataSource
#清除缓存
spring.thymeleaf.cache=false
#配置mapper
mybatis.mapper-locations=classpath:mapper/*.xml

5,在pojo下的新建类UserLogin

@Data
@AllArgsConstructor
@NoArgsConstructor

public class UserLogin {
    private String username;
    private String password;
}

6,建立数据库表userLogin
在这里插入图片描述
7.mapper层
新建UserLoginMapper接口

@Repository
@Mapper
public interface UserLoginMapper {
    //查询全部信息
    public List<UserLogin> findAll();
    //添加数据
    public int add(UserLogin userLogin);
    //根据name查询用户
    public UserLogin findByName(String username);
}

8,在resources目录下新建mapper目录,并在这个目录下新建UserLoginMapper.xml文件,并在application.properties中注册该组件,前面已经注册

<?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.luoke.springboot_mybatis.mapper.UserLoginMapper">
    <select id="findAll" resultType="com.luoke.springboot_mybatis.pojo.UserLogin">
        select * from userLogin
    </select>

    <insert id="add" parameterType="com.luoke.springboot_mybatis.pojo.UserLogin">
        insert into userLogin values (#{username},#{password})
    </insert>

    <select id="findByName" resultType="com.luoke.springboot_mybatis.pojo.UserLogin">
        select * from userLogin where username = #{username}
    </select>

</mapper>

9.测试

@SpringBootTest
class SpringbootMybatisApplicationTests {

    @Autowired
    private UserLoginMapper userLoginMapper;

    @Test
    void contextLoads() {
        List<UserLogin> all = userLoginMapper.findAll();
        System.out.println(all);
    }

    @Test
    public void findByName() {
        UserLogin userLogin = userLoginMapper.findByName("张三");
        System.out.println(userLogin);
    }

    @Test
    public void test() {
        UserLogin userLogin = new UserLogin("雒珂", "123");
        int add = userLoginMapper.add(userLogin);
        if (add == 1) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");

        }
    }
}

10.service层
在services下新建接口UserLoginServicesI和类UserLoginServicesImpl

UserLoginServicesI接口:

public interface UserService {
    //查询
    public List<UserLogin> queryAll();
    //添加数据
    public int add(UserLogin userLogin);
    //根据用户名查询数据
    public UserLogin queryByName(String username);
}

UserLoginServicesImpl类:

public class UserServiceImpl implements UserService {
    @Autowired
    UserLoginMapper userLoginMapper;
    @Override
    public List<UserLogin> queryAll() {
        return userLoginMapper.findAll();
    }

    @Override
    public int add(UserLogin userLogin) {
        return userLoginMapper.add(userLogin);
    }

    @Override
    public UserLogin queryByName(String username) {
        return userLoginMapper.findByName(username);
    }
}

11,conteoller层
编写MyController类

@Controller
public class MyController {
    @Autowired
    private UserService UserServiceImpl;

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/LoginSuccess")
    public String LoginSuccess(Model model, UserLogin userLogin){
        //先查询看该用户名是否存在
        UserLogin userLogin1 = UserServiceImpl.queryByName(userLogin.getUsername());
        if(userLogin1 != null){ //  如果查询的用户不为空
            System.out.println(userLogin1.toString());
            return "success";
        }
        else{
            //返回到登录页面
            model.addAttribute("data","该用户不存在,请先注册");
            return "login";
        }
    }
    //登录界面
    @RequestMapping("/toRegister")
    public String toRegister(){
        return "register";
    }
    @RequestMapping("/RegisterSuccess")
    public String toRegisterSuccess(Model model,UserLogin userLogin){
        //将账号密码加入到数据库中
        int add = UserServiceImpl.add(userLogin);
        System.out.println("数据插入成功!");
        model.addAttribute("data","注册成功,请登录!");
        return "login";
    }
}

12、编写前端页面

1、login.html:登录页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="background: aqua">
<div align="center">
    <br><br><h2>登录界面</h2><br><br>
    <span th:text="${data}" style="text-color:red;font-size: 10px"></span>
    <form method="get" action="/LoginSuccess">
        用户名:<input type="text" name="username" placeholder="请输入用户名" required/><br><br>
        密码:<input type="text" name="password" placeholder="请输入密码" required/><br><br>
        <input type="submit" value="登录">
    </form>
    <br>
    <form method="get" action="/toRegister">
        <input type="submit" value="注册">
    </form>
</div>
</body>
</html>

2、register.html:注册页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="background: aqua">
<div align="center">
    <br><br>注册界面<br><br>
    <form method="get" action="/RegisterSuccess">
        用户名:<input type="text" name="username" placeholder="请输入用户名" required/><br><br>
        密码:<input type="text" name="password" placeholder="请输入密码" required/><br><br>
        确认密码:<input type="text" name="password2" placeholder="请输入密码" required/><br><br>
        <input type="submit" value="注册">
    </form>
</div>
</body>
</html>

3、success.html:成功界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>success</h1>
</body>
</html>

13,运行一下
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦学java只为一口饭吃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值