mybatis全方位代码演练

​mybatis

1.mybatis快速入门

1.1. 复习jdbc

1.1.1jdbc连接数据库查询

 

 

package com.lhb;

import java.sql.*;

/**
 *@Auther:xs
 *@Date:2020/11/29
 *@Description:com.lhb
 *@Version:1.0
 */
public class TestJdbc {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        findArticle();
    }

    private static void findArticle() {

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/biblog";
            String userName = "root";
            String password = "root";
            conn = DriverManager.getConnection(url, userName, password);
            stmt = conn.createStatement();
            rs = stmt.executeQuery("select * from article");
            while (rs.next()) {
                System.out.println(rs.getInt(1));
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs == null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt == null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn == null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

 

1.1.2.jdbc事物提交

 

package com.lhb;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 *@Auther:xs
 *@Date:2020/11/29
 *@Description:com.lhb
 *@Version:1.0
 */
public class TestTransaction {
    public static void main(String[] args) {
        insertArticle();
    }

    private static void insertArticle() {
        String url = "jdbc:mysql://localhost:3306/biblog";
        String userName = "root";
        String password = "root";
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(url, userName, password);
            conn.setAutoCommit(false);

            String sql = "insert into article (atl_title, atl_desc, content) values(?, ?, ?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "title");
            ps.setString(2, "desc");
            ps.setString(3, "content");
            int i = ps.executeUpdate();
            System.out.println(i);
            conn.commit();
        } catch (ClassNotFoundException e) {
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (ps == null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn == null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

 

这里只单单一个查询(插入),写起来过于冗长,及其不利于扩展和编写业务。这就是我们需要学习框架的原因。

 

1.2 mybatis 搭建运行

搭建步骤:

1. 引入jar包

 

 

2.创建实体类

 


3.创建dao(mapper)

 

 

 

4. 创建sql映射文件

 

 

5. 创建mybatis主配置文件

 

 

6. 执行程序

 

 

package com.xs;

import com.xs.domain.Article;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * Hello world!
 *
 */
public class App
{
    public static void main( String[] args ) throws IOException {
        // 1. 配置文件
        String config = "mybatis.xml";
        // 2. 读取配置文件
        InputStream in = Resources.getResourceAsStream(config);
        // 3. 创建builder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        // 4. 使用builder读取配置文件创建factory
        SqlSessionFactory factory = builder.build(in);
        // 5. factory获取session对象
        SqlSession ss = factory.openSession();

        // 6. 指定要执行的sql语句的标识  sql映射文件中的namespace + "." + 标签的id值
        String sqlId = "com.xs.dao.ArticleDao.findAll";

        // 7.执行sql语句,通过sqlId找到sql语句
        List<Article> list = ss.selectList(sqlId);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        ss.close();
    }
}

 

注意: 这里我数据库表中带有驼峰字段名称,导致刚开始带像atl_id、atl_xxx这些字段,没有分装到对象的属性中。

这个问题可以在mybatis主配置中得到解决。

<configuration>
    <!-- 开启驼峰映射 ,为自定义的SQL语句服务-->
    <!--设置启用数据库字段下划线映射到java对象的驼峰式命名属性,默认为false-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

</configuration>

 

注意:

如果编译后的文件中没有.xml文件,需要去确认下idea resources文件夹是不是没有标识为资源文件夹

 

第二,是否pom.xml文件中存在插件

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

 

 

7. insert、update、delete 操作

insert

<insert id="insert" >
    insert into article
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="atlId != null">atl_id,</if>
        <if test="atlDesc != null">atl_desc,</if>
        <if test="atlTitle != null">atl_title,</if>
        <if test="isShow != null">is_show,</if>
        <if test="content != null">content,</if>
        <if test="atlTypeId != null">atl_type_id,</if>
        <if test="learnDoType != null">learn_do_type,</if>
    </trim>
    values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="atlId != null">#{atlId},</if>
        <if test="atlDesc != null">#{atlDesc},</if>
        <if test="atlTitle != null">#{atlTitle},</if>
        <if test="isShow != null">#{isShow},</if>
        <if test="content != null">#{content},</if>
        <if test="atlTypeId != null">#{atlTypeId},</if>
        <if test="learnDoType != null">#{learnDoType},</if>
    </trim>
</insert>

 

@Test
public void testInsert() throws IOException {
    String config = "mybatis.xml";
    InputStream in = Resources.getResourceAsStream(config);

    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(in);

    SqlSession session = factory.openSession();

    String sqlId = "com.xs.dao.ArticleDao.insert";
    Article article = new Article();
    article.setAtlDesc("insert");
    article.setAtlTitle("insert");
    article.setContent("insert");
    int i = session.insert(sqlId, article);
    session.commit();
    // mybatis默认不是自动提交事务的,所以执行修改操作后,需要手工提交事务
    System.out.println(i);
    session.close();
}

 

 

 

8. 配置日志功能

<settings>
    <!-- 开启驼峰映射 ,为自定义的SQL语句服务-->
    <!--设置启用数据库字段下划线映射到java对象的驼峰式命名属性,默认为false-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!--控制台中输出执行的sql语句和参数
        logImpl 表示要输出日志
        STDOUT_LOGGING 把日志输入到控制台上
    -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

 

 

2.mybatis dao代理

原理即是动态代理。

 

mybatis dao代理简述

我们前面的查询如下:
    String sqlId = "com.xs.dao.ArticleDao.findAll";
    List<Article> list = ss.selectList(sqlId);

通过SqlSession执行各种增、删、改、查。

这里的sqlId指的是mapper.xml文件中的namespace + “.” + 文件中sql的id。也就是说,我们需要一个mybatis.xml主配置,需要domain、mapper.xml(dao.xml)文件,唯独没有用到dao接口类,还没有写dao实现类。这是为什么?

那是因为,mybatis通过反射,通过dao接口类,得到dao接口的全限定名称com.xs.dao.ArticleDao,以及该接口中的方法findAll。这样,相当于通过dao接口类以及方法,就可以得到我们前面的sqlId,我们得到了sqlId,其实也就知道SqlSession执行的是增还是删,还是改,还是查了。也就是通过接口和接口中的方法,我们就可以拿到sqlId了。

 

有兴趣的朋友可以看看某位网友的这篇文章,是跟踪mybatis源码过程dao代理的实现。

 

https://blog.csdn.net/qq_23830637/article/details/103743697

 

package com.xs;


import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 *@Auther:xs
 *@Date:2020/12/3
 *@Description:com.xs
 *@Version:1.0
 */
public class Main {

    public static void main(String[] args) throws IOException {
        String config = "mybatis.xml";
        InputStream in = Resources.getResourceAsStream(config);
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        SqlSession sqlSession = factory.openSession();
        
        ArticleDao dao = sqlSession.getMapper(ArticleDao.class);
        dao.findAll();
    }

}

 

 

 

myProxyDao

我自己也模仿mybatis,改写了个超级简单的例子。看完动手写,你就知道mybatis的dao代理了。

 

 

 

package com.xs;


import com.xs.domain.Article;

/**
 *@Auther:xs
 *@Date:2020/12/3
 *@Description:com.xs
 *@Version:1.0
 */
public class Main {

    public static void main(String[] args) {
        ArticleDao dao = DaoProxyFactory.newInstance(ArticleDao.class);
        dao.findAll();
        System.out.println(dao);
        dao.insertArticle(new Article());
        System.out.println(dao);
        dao.getArticle();
    }

}

 

 

package com.xs;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 *@Auther:xs
 *@Date:2020/12/3
 *@Description:com.xs
 *@Version:1.0
 */
public class DaoProxyFactory {

    public static <T> T  newInstance(Class<T> type) {
        System.out.println(type.getName());
        T t = (T)Proxy.newProxyInstance(DaoProxyFactory.class.getClassLoader(), new Class[]{type}, (InvocationHandler) new DaoProxy(type));
        System.out.println(t + "........");
        return t;
    }

}

 

package com.xs;

import org.apache.ibatis.reflection.ExceptionUtil;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 *@Auther:xs
 *@Date:2020/12/5
 *@Description:com.xs
 *@Version:1.0
 */
public class DaoProxy implements InvocationHandler {
    private Class type;

    public DaoProxy(Class type) {
        this.type = type;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是 Object 定义的方法,直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);

            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // final MapperMethod mapperMethod = cachedMapperMethod(method);
        // return mapperMethod.execute(sqlSession, args);
        return myMethod(method, args);
    }

    private Object myMethod(Method method, Object[] args) {
        System.out.println("参数: " + args);
        System.out.println("类全限定名称: " + type.getName());
        System.out.println("方法名称: " + method.getName());
        return null;
    }


    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
            throws Throwable {
        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
                .getDeclaredConstructor(Class.class, int.class);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        final Class<?> declaringClass = method.getDeclaringClass();
        return constructor
                .newInstance(declaringClass,
                        MethodHandles.Lookup.PRIVATE| MethodHandles.Lookup.PROTECTED
                                | MethodHandles.Lookup.PACKAGE| MethodHandles.Lookup.PUBLIC)
                .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }

    private boolean isDefaultMethod(Method method) {//  判断不是abstract,也不是static的public方法
        return (method.getModifiers()
                & (Modifier.ABSTRACT| Modifier.PUBLIC| Modifier.STATIC)) == Modifier.PUBLIC
                && method.getDeclaringClass().isInterface();
    }
}

 

package com.xs;

import com.xs.domain.Article;

import java.util.List;

/**
 *@Auther:xs
 *@Date:2020/12/5
 *@Description:com.xs
 *@Version:1.0
 */
public interface ArticleDao {

    public List<Article> findAll();

    public Integer insertArticle(Article article);

    default void getArticle() {
        System.out.println("空数据");
    }
}

 

 

3.传入参数

1.parameterType

mapper文件中sql元素的一个属性,表示dao接口中方法参数的数据类型

 

<select id="getById" parameterType="Integer" resultType="com.xs.domain.ArticleType">
    select atl_type_id,
    atl_type_code,
    atl_type_name,
    parent_id,
    sort
    from article_type
    where atl_type_id = #{id}
</select>

 

public interface ArticleTypeDao {
    public List<ArticleType> findAll();

    public ArticleType getById(Integer id);

    public int add(ArticleType articleType);
}

 

parameterType不是强制的,可以不写

 

2.一个简单类型的参数

什么是mybatis所说的【一个简单类型的参数】,即一个参数,这参数可以是java基本类型以及基本类型自动装箱的对象类型或String类型,那么在mapper中可以是#{任意字符}

3.多个参数-使用@Param

public ArticleType getByParams(@Param("id") Integer id, @Param("code")String code);

<select id="getByParams" resultType="com.xs.domain.ArticleType">
    select atl_type_id, atl_type_code, atl_type_name, parent_id, sort
    from article_type
    where atl_type_id = #{id} and atl_type_code = #{code}
</select>

 

4.多个参数-使用java对象作为参数值

使用对象的语法 #{属性名, javaType=类型名称,jdbcType=数据类型}

#{atlTypeCode, javaType=java.lang.String, jdbcType=VARCHAR}

public ArticleType getByObj(ArticleType articleType);

 

<select id="getByObj" resultType="com.xs.domain.ArticleType">
    select atl_type_id, atl_type_code, atl_type_name, parent_id, sort
    from article_type
    where atl_type_id = #{atlTypeId} and atl_type_code = #{atlTypeCode}
</select>

 

 

5.多个参数-按位置传值(不重要、不推荐)

#{arg0} #{arg1}...

按照位置传值

public ArticleType getByLocate(Integer id, String code);

 

<select id="getByLocate" resultType="com.xs.domain.ArticleType">
    select atl_type_id, atl_type_code, atl_type_name, parent_id, sort
    from article_type
    where atl_type_id = #{arg0} and atl_type_code = #{arg1}
</select>

 

6.多个参数-使用map(不重要、不推荐)

public ArticleType getByMap(Map map);

 

<select id="getByMap" resultType="com.xs.domain.ArticleType">
    select atl_type_id, atl_type_code, atl_type_name, parent_id, sort
    from article_type
    where atl_type_id = #{atlTypeId} and atl_type_code = #{atlTypeCode}
</select>

 

 

 

7.#和$的区别

#: 占位符,告诉mybatis使用实际的参数值代替。并使用PprepareStatement对象执行sql语句

 

$: 字符串替换,告诉mybatis使用Statement把sql语句和参数进行字符串拼接。${xxx}表示参数替换的位置。

#的执行语句:

Preparing: select atl_type_id, atl_type_code, atl_type_name, parent_id, sort from article_type whereatl_type_id = ?

 

$的执行语句:

Preparing: select atl_type_id, atl_type_code, atl_type_name, parent_id, sort from article_type whereatl_type_id = 1

 

8. like

用法1

public List<ArticleType> likeWithCode(String code);

 

<select id="likeWithCode" resultType="com.xs.domain.ArticleType">
    select * from article_type where atl_type_code like #{code}
</select>

 

用法2

public List<ArticleType> likeWithCodeInMapper(String code);

 

<select id="likeWithCodeInMapper" resultType="com.xs.domain.ArticleType">
    select * from article_type where atl_type_code like "%" #{code} "%"
</select>

 

4.封装mybatis输出结果

1.resultType

值sql语句执行完毕后,数据转为java对象。

处理方式:

1.mybatis执行sql语句,然后mybatis调用类的无参数构造方法,创建对象。

2.mybatis把ResultSet指定列值赋予同名属性。

 

resultType结果类型的值,可以是类型的全限定名称、类型的别名。

 

自定义类型别名步骤:

1. 在mybatis主配置文件中定义,使用<typeAlias>定义别名

2.在resultType中就可以使用自定义别名了

 

 

自定义别名的两种用法

1. 给每个类起别名

 

<typeAliases>
    <typeAlias type="com.xs.domain.ArticleType" alias="articleType"></typeAlias>
</typeAliases>

 

<select id="findAll" resultType="articleType">
    select atl_type_id,
    atl_type_code,
    atl_type_name,
    parent_id,
    sort
    from article_type
    order by atl_type_id
</select>

 

2.给包起别名

 

    <typeAliases>
<!--        <typeAlias type="com.xs.domain.ArticleType" alias="articleType"></typeAlias>-->
        <package name="com.xs.domain"/>
    </typeAliases>

 

<select id="findAll" resultType="ArticleType">
    select atl_type_id,
    atl_type_code,
    atl_type_name,
    parent_id,
    sort
    from article_type
    order by atl_type_id
</select>

 

1.简单类型

public int selectCount();

 

<select id="selectCount" resultType="int">
    select count(1) from article_type
</select>

 

 

2.对象类型

public ArticleType getById(Integer id);

 

<select id="getById" parameterType="Integer" resultType="com.xs.domain.ArticleType">
    select atl_type_id,
    atl_type_code,
    atl_type_name,
    parent_id,
    sort
    from article_type
    where atl_type_id = #{id}
</select>

 

3.Map

public Map findOneToMap();

 

<select id="findOneToMap" resultType="map">
    select * from article_type where atl_type_id = 1 order by atl_type_id
</select>

 

 

2.resultMap

结果映射,指定列名和java对象的属性对应关系

1.自定义列值赋值给某个属性

 

2.当你的列名和属性名不一样时使用。

 

public ArticleType getForResultMap(Integer id);

 

<resultMap id="articleMap" type="com.xs.domain.ArticleType">
    <!--主键列使用id标签,其他列使用result-->
    <!--
        column: 列名
        property: java类型的属性名
    -->
    <id column="atl_type_id" property="atlTypeId"/>
    <result column="atl_type_code" property="atlTypeCode"/>
    <result column="atl_type_name" property="atlTypeName"/>
    <result column="parent_id" property="parentId"/>
    <result column="sort" property="sort"/>

</resultMap>

<select id="getForResultMap" resultMap="articleMap">
    select atl_type_id, atl_type_code, atl_type_name, parent_id, sort
    from article_type
    where atl_type_id = #{id}
</select>

 

 

 

5.mybatis 动态sql

动态sql使用的是mybatis提供的标签,<if><where><foreach>

<if test=””>

</if>

 

1. if标签

List<ArticleType> selectWithIf(@Param("atlTypeCode") String atlTypeCode);

 

<select id="selectWithIf" resultType="com.xs.domain.ArticleType">
    select * from article_type
    where 2 > 1
    <if test="atlTypeCode != null and atlTypeCode != ''">
        and atl_type_code = #{atlTypeCode}
    </if>
</select>

 


2.where 标签

List<ArticleType> selectWithWhere(@Param("atlTypeCode") String atlTypeCode);

 

<select id="selectWithWhere" resultType="com.xs.domain.ArticleType">
    select * from article_type
    <where>
        <if test="atlTypeCode != null and atlTypeCode != ''">
            and atl_type_code = #{atlTypeCode}
        </if>
    </where>
</select>

 

3.foreach标签

循环java中的数组, list集合的。主要是用在sql的in语句中。

 

<foreach collection="" item="" open="" close="" separator="">
</foreach>

 

collection: 表示接口中的方法参数的类型,如果是数组使用array,list则使用list

item: 自定义的遍历

open: 循环开始时的字符

close: 循环结束时的字符

separator: 集合成员之间的分隔符


1. 集合存放简单对象

 

List<ArticleType> selectWithForeach(@Param("ids") List<Integer> ids);

 

<select id="selectWithForeach" resultType="com.xs.domain.ArticleType">
    select * from article_type
    <where>
        <if test="ids != null and ids.size > 0">
        atl_type_id in
            <foreach collection="ids" item="i" open="(" close=")" separator=",">
                #{i}
            </foreach>
        </if>
    </where>
</select>

 

2. 集合存放复杂对象

List<ArticleType> selectWithForeachComplicated(@Param("params") List<ArticleType> params);

 

<select id="selectWithForeachComplicated" resultType="com.xs.domain.ArticleType">
    select * from article_type
    <where>
        <if test="params != null and params.size > 0">
            (atl_type_id, atl_type_code) in
            <foreach collection="params" item="article" open="(" close=")" separator=",">
                (#{article.atlTypeId}, #{article.atlTypeCode})
            </foreach>
        </if>
    </where>
</select>

 

 

4. 代码片段

<sql id="selectTable">
    select * from article_type
</sql>
<select id="selectWithForeachComplicated" resultType="com.xs.domain.ArticleType">
    <include refid="selectTable"></include>
    <where>
        <if test="params != null and params.size > 0">
            (atl_type_id, atl_type_code) in
            <foreach collection="params" item="article" open="(" close=")" separator=",">
                (#{article.atlTypeId}, #{article.atlTypeCode})
            </foreach>
        </if>
    </where>
</select>

6.mybatis配置

mybatis配置都可以去官网查看,有中文的文档。这里列几个看看。

 

1. 配置文件读取

 

<properties resource="jdbc.properties"/>

 

<environment id="development">
    <transactionManager type="JDBC"></transactionManager>
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.pwd}"/>
    </dataSource>
</environment>

2. mappers文件读取

读取有两种方式

1. 直接读取文件

<mappers>
    <mapper resource="com/xs/dao/ArticleTypeDao.xml"/>
</mappers>

 

2.读取包

使用package有要求:

1. mapper文件名称需要跟接口名称一样,区分大小写

2.mapper文件和dao接口需要在同一目录

    <mappers>
<!--        <mapper resource="com/xs/dao/ArticleTypeDao.xml"/>-->
        <package name="com.xs.dao"/>
    </mappers>

 

 

7.扩展pagehelp

1. 引入jar包

<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.1.10</version>
</dependency>

 

2.加入plugin配置

在主配置文件中,environments前加入plugin

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

 

 

3. 使用查询页

这里表示从第一页开始取,取5条数据。第一个参数是页数,第二个是页大小,页数0跟1都是第一页。只需要在查询之前,使用PageHelper.startPage方法即可。

SqlSession sqlSession = MyBatisUtils.getSqlSession();

ArticleTypeDao dao = sqlSession.getMapper(ArticleTypeDao.class);

PageHelper.startPage(1,5);
dao.selectPage();

 

List<ArticleType> selectPage();

 

<select id="selectPage" resultType="com.xs.domain.ArticleType">
    <include refid="selectTable"></include> order by atl_type_id
</select>

 

 

 


版权声明:本文为m0_53051494原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。