美文网首页
Mybatis编程式开发实战

Mybatis编程式开发实战

作者: 名字是乱打的 | 来源:发表于2020-03-07 22:46 被阅读0次

    参照mybatis3中文文档
    入门以及配置还有动态sql等等全部都有,非常详细
    建议大家别看我的,直接看mybats中文文档. 最起码要把mybatis入门仔细研读一遍

    1.首先引入pom里引入mybatis ,mysql
     <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
    <!--
                <version>3.3.0-SNAPSHOT</version>
    -->
                <version>3.5.1</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.21</version>
            </dependency>
    
    2.配置数据库和mybttis

    单独做个配置文件db.properties,方便后期维护

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/gp-mybatis?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
    jdbc.username=root
    jdbc.password=123456
    
    <?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 resource="db.properties"></properties>
        <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="STDOUT_LOGGING" />
    
            <!-- 控制全局缓存(二级缓存)-->
            <setting name="cacheEnabled" value="true"/>
    
            <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
            <setting name="aggressiveLazyLoading" value="false"/>
            <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
            <!--<setting name="proxyFactory" value="CGLIB" />-->
            <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
            <!--
                    <setting name="localCacheScope" value="STATEMENT"/>
            -->
            <setting name="localCacheScope" value="SESSION"/>
        </settings>
    
        <typeAliases>
            <typeAlias alias="blog" type="com.gupaoedu.domain.Blog" />
        </typeAliases>
    
    <!--    <typeHandlers>
            <typeHandler handler="com.gupaoedu.type.MyTypeHandler"></typeHandler>
        </typeHandlers>-->
    
        <!-- 对象工厂 -->
    <!--    <objectFactory type="com.gupaoedu.objectfactory.GPObjectFactory">
            <property name="gupao" value="666"/>
        </objectFactory>-->
    
    <!--    <plugins>
            <plugin interceptor="com.gupaoedu.interceptor.SQLInterceptor">
                <property name="gupao" value="betterme" />
            </plugin>
            <plugin interceptor="com.gupaoedu.interceptor.MyPageInterceptor">
            </plugin>
        </plugins>-->
    
    <!--environment 元素体中包含了事务管理和连接池的配置。-->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>\
    
    <!--mappers 元素则是包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。-->
        <mappers>
            <mapper resource="BlogMapper.xml"/>
            <mapper resource="BlogMapperExt.xml"/>
        </mappers>
    
    </configuration>
    

    3.mybatis的使用

    1.API方式
    SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
    如下面代码块中的com.gupaoedu.mapper.BlogMapper.selectBlogById

    com.gupaoedu.mapper.BlogMapper.selectBlogById映射的sql语句
       /**
         * 使用MyBatis API方式
         * @throws IOException
         */
        @Test
        public void testStatement() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            SqlSession session = sqlSessionFactory.openSession();
            try {
                Blog blog = (Blog) session.selectOne("com.gupaoedu.mapper.BlogMapper.selectBlogById", 1);
                System.out.println(blog);
            } finally {
                session.close();
            }
        }
    
    sqlsession的众多API可以直接执行sql语句

    2.通过 SqlSession.getMapper(XXXMapper.class) 接口方式

    API这种方式虽然能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉。但是现在有了一种更简洁的方式 ——使用正确描述每个语句的参数和返回值的接口
    (比如 BlogMapper.class),我们不仅可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。

    
        /**
         * 通过 SqlSession.getMapper(XXXMapper.class)  接口方式
         * @throws IOException
         */
        @Test
        public void testSelect() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
            try {
                BlogMapper mapper = session.getMapper(BlogMapper.class);
                Blog blog = mapper.selectBlogById(1);
                System.out.println(blog);
            } finally {
                session.close();
            }
        }
    

    sqlsesss.getmapper(BlogMapper.class)里的mapper接口

    
    public interface BlogMapper {
        /**
         * 根据主键查询文章
         * @param bid
         * @return
         */
        public Blog selectBlogById(Integer bid);
    
        /**
         * 根据实体类查询文章
         * @param blog
         * @return
         */
        public List<Blog> selectBlogByBean(Blog blog );
    
        /**
         * 文章列表翻页查询
         * @param rowBounds
         * @return
         */
        public List<Blog> selectBlogList(RowBounds rowBounds);
    
        /**
         * 更新博客
         * @param blog
         * @return
         */
        public int updateByPrimaryKey(Blog blog);
    
        /**
         * 新增博客
         * @param blog
         * @return
         */
        public int insertBlog(Blog blog);
    
        /**
         * 根据博客查询作者,一对一,嵌套结果
         * @param bid
         * @return
         */
        public BlogAndAuthor selectBlogWithAuthorResult(Integer bid);
    
        /**
         * 根据博客查询作者,一对一,嵌套查询,存在N+1问题
         * @param bid
         * @return
         */
        public BlogAndAuthor selectBlogWithAuthorQuery(Integer bid);
    
        /**
         * 查询文章带出文章所有评论(一对多)
         * @param bid
         * @return
         */
        public BlogAndComment selectBlogWithCommentById(Integer bid);
    
        /**
         * 查询作者带出博客和评论(多对多)
         * @return
         */
        public List<AuthorAndBlog> selectAuthorWithBlog();
    
        List<Blog> selectByExample(BlogExample example);
    
    }
    

    BlogMapper.xml,真正的sql语句,映射接口的sql语句

    只有接口肯定是不行的,这里的xml是真正的sql语句
    为了这个简单的例子,我们似乎在这个文件头上写了不少配置,但实际上它并不多。在一个 XML 映射文件中,可以定义无数个映射语句,这样一来,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.gupaoedu.mapper.BlogMapper">
        <!-- 声明这个namespace使用二级缓存 -->
        <cache/>
    
        <!-- 使用Redis作为二级缓存 -->
    <!--
        <cache type="org.mybatis.caches.redis.RedisCache"
               eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
    -->
    
    <!--        <cache type="org.apache.ibatis.cache.impl.PerpetualCache"
                   size="1024"
                   eviction="LRU"
                   flushInterval="120000"
                   readOnly="false"/>-->
    
        <resultMap id="BaseResultMap" type="blog">
            <id column="bid" property="bid" jdbcType="INTEGER"/>
    <!--
            <result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.gupaoedu.type.MyTypeHandler"/>
    -->
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <result column="author_id" property="authorId" jdbcType="INTEGER"/>
        </resultMap>
    
        <!-- 根据文章查询作者,一对一查询的结果,嵌套查询 -->
        <resultMap id="BlogWithAuthorResultMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
            <id column="bid" property="bid" jdbcType="INTEGER"/>
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <!-- 联合查询,将author的属性映射到ResultMap -->
            <association property="author" javaType="com.gupaoedu.domain.Author">
                <id column="author_id" property="authorId"/>
                <result column="author_name" property="authorName"/>
            </association>
        </resultMap>
    
        <!-- 另一种联合查询(一对一)的实现,但是这种方式有“N+1”的问题 -->
        <resultMap id="BlogWithAuthorQueryMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
            <id column="bid" property="bid" jdbcType="INTEGER"/>
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <association property="author" javaType="com.gupaoedu.domain.Author"
                         column="author_id" select="selectAuthor"/> <!-- selectAuthor 定义在下面-->
        </resultMap>
    
        <!--  查询文章带评论的结果(一对多) -->
        <resultMap id="BlogWithCommentMap" type="com.gupaoedu.domain.associate.BlogAndComment" extends="BaseResultMap" >
            <collection property="comment" ofType="com.gupaoedu.domain.Comment">
                <id column="comment_id" property="commentId" />
                <result column="content" property="content" />
            </collection>
        </resultMap>
    
        <!--  按作者查询文章评论的结果(多对多) -->
        <resultMap id="AuthorWithBlogMap" type="com.gupaoedu.domain.associate.AuthorAndBlog" >
            <id column="author_id" property="authorId" jdbcType="INTEGER"/>
            <result column="author_name" property="authorName" jdbcType="VARCHAR"/>
            <collection property="blog" ofType="com.gupaoedu.domain.associate.BlogAndComment">
                <id column="bid" property="bid" />
                <result column="name" property="name" />
                <result column="author_id" property="authorId" />
                <collection property="comment" ofType="com.gupaoedu.domain.Comment">
                    <id column="comment_id" property="commentId" />
                    <result column="content" property="content" />
                </collection>
            </collection>
        </resultMap>
    
        <select id="selectBlogById" resultMap="BaseResultMap" statementType="PREPARED" useCache="false">
            select * from blog where bid = #{bid}
        </select>
    
        <!-- $只能用在自定义类型和map上 -->
        <select id="selectBlogByBean"  parameterType="blog" resultType="blog" >
            select bid, name, author_id authorId from blog where name = '${name}'
        </select>
    
        <select id="selectBlogList" resultMap="BaseResultMap" >
            select bid, name, author_id authorId from blog
        </select>
    
        <!-- 根据文章查询作者,一对一,嵌套结果,无N+1问题 -->
        <select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap" >
            select b.bid, b.name, b.author_id, a.author_id , a.author_name
            from blog b
            left join author a
            on b.author_id=a.author_id
            where b.bid = #{bid, jdbcType=INTEGER}
        </select>
    
        <!-- 根据文章查询作者,一对一,嵌套查询,存在N+1问题,可通过开启延迟加载解决 -->
        <select id="selectBlogWithAuthorQuery" resultMap="BlogWithAuthorQueryMap" >
            select b.bid, b.name, b.author_id, a.author_id , a.author_name
            from blog b
            left join author a
            on b.author_id=a.author_id
            where b.bid = #{bid, jdbcType=INTEGER}
        </select>
    
        <!-- 嵌套查询 -->
        <select id="selectAuthor" parameterType="int" resultType="com.gupaoedu.domain.Author">
            select author_id authorId, author_name authorName
            from author where author_id = #{authorId}
        </select>
    
        <!-- 根据文章查询评论,一对多 -->
        <select id="selectBlogWithCommentById" resultMap="BlogWithCommentMap" >
            select b.bid, b.name, b.author_id authorId, c.comment_id commentId, c.content
            from blog b, comment c
            where b.bid = c.bid
            and b.bid = #{bid}
        </select>
    
        <!-- 根据作者文章评论,多对多 -->
        <select id="selectAuthorWithBlog" resultMap="AuthorWithBlogMap" >
            select b.bid, b.name, a.author_id authorId, a.author_name authorName, c.comment_id commentId, c.content
            from blog b, author a, comment c
            where b.author_id = a.author_id and b.bid = c.bid
        </select>
    
        <!-- 手动实现翻页 -->
    <!--    <select id="selectBlogPage" parameterType="map" resultMap="BaseResultMap">
            select * from blog limit #{curIndex} , #{pageSize}
        </select>-->
    
        <update id="updateByPrimaryKey" parameterType="blog">
            update blog
            set name = #{name,jdbcType=VARCHAR}
            where bid = #{bid,jdbcType=INTEGER}
        </update>
    
        <insert id="insertBlog" parameterType="blog">
        insert into blog (bid, name, author_id)
        values (#{bid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{authorId,jdbcType=INTEGER})
    </insert>
        <!--
        values (#{bid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.type.MyTypeHandler}, #{authorId,jdbcType=CHAR})
        -->
    
        <!-- 自动生成的Example -->
        <sql id="Base_Column_List">
            bid, name, author_id
        </sql>
        <sql id="Example_Where_Clause">
            <where>
                <foreach collection="oredCriteria" item="criteria" separator="or">
                    <if test="criteria.valid">
                        <trim prefix="(" prefixOverrides="and" suffix=")">
                            <foreach collection="criteria.criteria" item="criterion">
                                <choose>
                                    <when test="criterion.noValue">
                                        and ${criterion.condition}
                                    </when>
                                    <when test="criterion.singleValue">
                                        and ${criterion.condition} #{criterion.value}
                                    </when>
                                    <when test="criterion.betweenValue">
                                        and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                                    </when>
                                    <when test="criterion.listValue">
                                        and ${criterion.condition}
                                        <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                                            #{listItem}
                                        </foreach>
                                    </when>
                                </choose>
                            </foreach>
                        </trim>
                    </if>
                </foreach>
            </where>
        </sql>
        <select id="selectByExample" parameterType="com.gupaoedu.domain.BlogExample" resultMap="BaseResultMap">
            select
            <if test="distinct">
                distinct
            </if>
            'true' as QUERYID,
            <include refid="Base_Column_List" />
            from blog
            <if test="_parameter != null">
                <include refid="Example_Where_Clause" />
            </if>
            <if test="orderByClause != null">
                order by ${orderByClause}
            </if>
        </select>
    </mapper>
    

    你可能注意到这和使用完全限定名调用 Java 对象的方法Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);类似。这样,该命名就可以直接映射到在命名空间中同名的 Mapper 类,并将已映射的 select 语句中的名字、参数和返回类型匹配成方法。 因此你就可以像上面那样很容易地调用这个对应 Mapper 接口的方法,就像下面这样: 明明空间类似于类,下面语句中的id则是方法名.另外这个id之所以没用些全路径是因为我们的mabtis.xml里配置了别名<typeAliases>

    BlogMapper mapper = session.getMapper(BlogMapper.class);
    Blog blog = mapper.selectBlog(101);
    

    这种方法有些优势,在mybatis中文文档中已声明首先它不依赖于字符串字面值,会更安全一点; 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择已映射的 SQL 语句。

    三 编程式开发的核心对象

    下面核心对象的介绍的话,上面已经说了不少了,mybatis中文档里也有解释
    - SqlSessionFactoryBuilder
    - SqlSessionFactory
    - SqlSession
    - Mapper

    四 Mybatis编程式开发,各核心对象作用域(Scope)和生命周期

    理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题

    提示 对象生命周期和依赖注入框架
    依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架来使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。

    SqlSessionFactoryBuilder

    这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放给更重要的事情。

    SqlSessionFactory

    SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在我们在使用过程中要创建很多的sqlsession会话,因此没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,一般我们生产中会设计成单例的,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

    SqlSession

    每个线程都应该有它自己的 SqlSession 实例SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

    try (SqlSession session = sqlSessionFactory.openSession()) {
      // 你的应用逻辑代码
    }
    

    在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。

    映射器实例

    映射器是一些由你创建的、绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。下面的示例就展示了这个实践:

    try (SqlSession session = sqlSessionFactory.openSession()) {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      // 你的应用逻辑代码
    }
    

    MyBatis 的配置

    相关文章

      网友评论

          本文标题:Mybatis编程式开发实战

          本文链接:https://www.haomeiwen.com/subject/toghdhtx.html