<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>MyBatis &#8211; Blog of Code</title>
	<atom:link href="https://www.cztcode.com/category/java/mybatis/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.cztcode.com</link>
	<description></description>
	<lastBuildDate>Mon, 13 Jul 2020 16:12:07 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.cztcode.com/wp-content/uploads/2024/02/cropped-logo-32x32.webp</url>
	<title>MyBatis &#8211; Blog of Code</title>
	<link>https://www.cztcode.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">217219486</site>	<item>
		<title>MyBatis学习总结（八）</title>
		<link>https://www.cztcode.com/2020/3111/</link>
					<comments>https://www.cztcode.com/2020/3111/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Mon, 13 Jul 2020 16:12:07 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3111</guid>

					<description><![CDATA[MyBatis学习总结（八） 整合Log4j、延迟加载、查询缓存]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">整合Log4j</h2>



<p class="is-style-iw-2em">通过日志信息，可以阅读mybatis执行情况，观察mybatis实际执行sql语句以及SQL中的参数和返回结果。</p>



<p class="is-style-iw-2em">常见日志有这些：SLF4J 、Apache Commons Logging、Log4j2、Log4j 、JDK logging。这里我们使用Log4j。</p>



<p class="is-style-iw-2em">第一步是导入jar包。mybatis.zip文件中的lib目录下有log4j的jar包，如果没有的可以自行去下载。</p>



<p class="is-style-iw-2em">第二步是在conf中开启日志。在settings标签中开启：</p>



<pre class="wp-block-preformatted"> &lt;settings&gt;
<em>&lt;!--        开启日志，并指定使用的日志--&gt;
        </em>&lt;setting name="logImpl" value="LOG4J"/&gt;
    &lt;/settings&gt;</pre>



<p class="is-style-iw-2em">value属性指定了使用的日志类型，如果不指定，MyBatis就会按照以下的顺序去寻找：</p>



<pre class="wp-block-code"><code>SLF4J →Apache Commons Logging→Log4j2→Log4j → JDK logging</code></pre>



<p class="is-style-iw-2em">尤其要注意name属性的内容严格区分大小写。</p>



<p class="is-style-iw-2em">第三步编写配置日志输出文件log4j.properties</p>



<pre class="wp-block-preformatted">log4j.rootLogger=DEBUG, stdout<br>log4j.appender.stdout=org.apache.log4j.ConsoleAppender<br>log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<br>log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n</pre>



<p class="is-style-iw-2em">第一行表示日志输出debug类型的信息，且输出到标准输出流中，日志级别DEBUG&lt; INFO&lt;WARN&lt;ERROR；第二行表明日志显示的方式是追加显示到控制台中；第三行表示日志显示的样式，可以是平铺的也可以是竖列的；最后一行表示日志信息的输出格式。后面三行不重要。</p>



<p class="is-style-iw-2em">log4j.properties文件名必须和使用的日志的jar包类型一致，因为在setting中指定了value之后MyBatis会自动寻找这个log4j.properties文件。</p>



<h2 class="wp-block-heading">延迟加载</h2>



<p class="is-style-iw-2em">延迟加载是指在一对一、一对多、多对一、多对多等设计多个表的查询时，首先查询指定的表的结果，延时查询所关联的表的结果。只有当需要用到关联表的结果时（在java中使用到了所关联表的实体类对象），MyBatis才会根据指定查询的表的外键去查询所关联表的结果。意思是原本查询结果涉及两个表，需要用一个sql语句执行完，查询的范围也是两张表的所有元组，现在延时加载后将一个sql语句分为两个分别查询两张表的语句，首先只执行一个，当需要用到第二个表的结果时再去查询第二个。</p>



<p class="is-style-iw-2em">首先需要开启延迟加载。延迟加载需要手动开启，在conf.xml文件中要先关闭立即加载，再开启延迟加载：</p>



<pre class="wp-block-preformatted">&lt;settings&gt;
<em>...</em>
        &lt;setting name="lazyLoadingEnabled" value="true"/&gt;
        &lt;setting name="aggressiveLazyLoading" value="false"/&gt;
    &lt;/settings&gt;</pre>



<h3 class="wp-block-heading">先以一对一为例</h3>



<p class="is-style-iw-2em">在Mapper文件中，原来的查询是这样的：</p>



<pre class="wp-block-preformatted"><em>&lt;!--    </em><em>使用</em><em>MyBatis</em><em>提供的</em><em>resultMap</em><em>来实现一对一</em><em>--&gt;<br></em><em>    </em>&lt;select id="queryStudentWithHashMapForFk" parameterType="int" resultMap="person_idCard_map"&gt;<br>        select  p.*,c.*<br>        from person p inner join personidcard c on p.cardid =c.cardid<br>        where p.id=#{id}<br>    &lt;/select&gt;<br>    <br>    &lt;resultMap id="person_idCard_map" type="Person"&gt;<br>        &lt;id property="id" column="id"/&gt;<br>        &lt;result property="name" column="name"/&gt;<br>        &lt;result property="age" column="age"/&gt;<br><em>&lt;!--        </em><em>对象成员时，如果是一对一映射，就用</em><em>association;javaType</em><em>指定改属性的类型</em><em>--&gt;<br></em><em>        </em>&lt;association property="personIdCard" javaType="PersonIdCard"&gt;<br>            &lt;id property="cardid" column="cardid"/&gt;<br>            &lt;result property="cardinfo" column="cardinfo"/&gt;<br>        &lt;/association&gt;<br>    &lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">延迟加载的语句如下：</p>



<pre class="wp-block-preformatted"> <em>&lt;!--    利用延时加载来实现一对一--&gt;
    </em>&lt;select id="queryStudentWithLazyloading" resultMap="person_idCard_lazyload_map"&gt;
        select * from person
    &lt;/select&gt;

    &lt;resultMap id="person_idCard_lazyload_map" type="Person"&gt;
        &lt;id property="id" column="id"/&gt;
        &lt;result property="name" column="name"/&gt;
        &lt;result property="age" column="age"/&gt;
        <em>&lt;!-- 这里采用延迟加载：在查询人时，并不立即加载身份证--&gt;
        </em>&lt;association property="personIdCard" javaType="PersonIdCard" select="shopkeeper.mapper.personIdCardMapper.queryCardById" column="cardid"&gt;<em>
        </em>&lt;/association&gt;
    &lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">改变的地方有两处，一是sql语句，现在只需要先查询person表的信息了；二是在resultMap中的association标签，personIdCard这张表并不是直接写出要查询的元素，而是多了select和column两个属性：select表示延时加载时执行的查询语句在mapper文件中的id值，column是这两张表的关联，即外键，作为参数传入select中的查询语句里（column写入的是select标签中查询的表的外键，这里的cardid在两张表中名字是相同的，在一对多的例子中就不相同了）。select引用的语句如下：</p>



<pre class="wp-block-preformatted">&lt;select id="queryCardById" parameterType="int" resultType="personIdCard"&gt;<br>   select <em>* </em>from personidcard where cardid =#{cardid}<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">也就是说，一开始我们只得到了person表的查询结果，只有当我们要用到personIdCard的结果时，MyBatis才会动用延时加载来根据外键来找到personIdCard的结果。</p>



<p class="is-style-iw-2em">可以发现的时select中的语句在另外一个Mapper中，其实写在一个Mapper里也是可以的，但是要遵循一个实体类对应一个Mapper文件的开发习惯。新建了一个mapper文件后一定要记得去conf.xml文件中建立映射。</p>



<p class="is-style-iw-2em">在测试类中检测延时加载的效果：</p>



<pre class="wp-block-preformatted">public static void queryStudentWithLazyloading() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br><br><br>    List&lt;Person&gt; pers=personMapper.queryStudentWithLazyloading();<br><br>    for (Person person:pers)<br>    {<br>        System.<em>out</em>.println(person.getId()+", "+person.getName());<br>    }<br><br>    for (Person person:pers)<br>    {<br><br>        PersonIdCard card=person.getPersonIdCard();<br><br>        System.<em>out</em>.println(card.getCardid()+", "+card.getCardinfo());<br>    }<br><br>    sqlSession.close();<br>    reader.close();<br>}</pre>



<p class="is-style-iw-2em">我是先得到了所有的person表的查询结果，再根据每一个person来获取其idcard的信息。日志输出如下：</p>



<pre class="wp-block-code"><code>DEBUG &#091;main] - Opening JDBC Connection
DEBUG &#091;main] - Created connection 359922172.
DEBUG &#091;main] - Setting autocommit to false on JDBC Connection &#091;com.mysql.cj.jdbc.ConnectionImpl@1573f9fc]
DEBUG &#091;main] - ==&gt;  Preparing: select * from person
DEBUG &#091;main] - ==&gt; Parameters: 
DEBUG &#091;main] - &lt;==      Total: 4
1, 刘茜元
2, 陈曦
3, 刘闹闹
13, 刘咯咯
DEBUG &#091;main] - ==&gt;  Preparing: select * from personidcard where cardid =?
DEBUG &#091;main] - ==&gt; Parameters: 1(Integer)
DEBUG &#091;main] - &lt;==      Total: 1
1, 刘茜元 info
DEBUG &#091;main] - ==&gt;  Preparing: select * from personidcard where cardid =?
DEBUG &#091;main] - ==&gt; Parameters: 2(Integer)
DEBUG &#091;main] - &lt;==      Total: 1
2, 陈曦 info
DEBUG &#091;main] - ==&gt;  Preparing: select * from personidcard where cardid =?
DEBUG &#091;main] - ==&gt; Parameters: 3(Integer)
DEBUG &#091;main] - &lt;==      Total: 1
3, 刘闹闹 info
DEBUG &#091;main] - ==&gt;  Preparing: select * from personidcard where cardid =?
DEBUG &#091;main] - ==&gt; Parameters: 13(Integer)
DEBUG &#091;main] - &lt;==      Total: 1
13, 刘咯咯 info
DEBUG &#091;main] - Resetting autocommit to true on JDBC Connection &#091;com.mysql.cj.jdbc.ConnectionImpl@1573f9fc]
DEBUG &#091;main] - Closing JDBC Connection &#091;com.mysql.cj.jdbc.ConnectionImpl@1573f9fc]
DEBUG &#091;main] - Returned connection 359922172 to pool.
</code></pre>



<p class="is-style-iw-2em">可以很明显地看出查询身份证的操作分了多次进行，且完全在查询人的操作之后。</p>



<h3 class="wp-block-heading">一对多</h3>



<p class="is-style-iw-2em">和一对一的延迟加载配置方法相同。</p>



<p class="is-style-iw-2em">在personClassMapper中建立延时加载的查询：</p>



<pre class="wp-block-preformatted">&lt;select id="queryClassAndPersonsWithLazyloading" resultMap="class_person_Lazyload_map"&gt;<br>    select c.* from personclass c<br>&lt;/select&gt;<br><br>&lt;resultMap id="class_person_Lazyload_map" type="personclass"&gt;<br>    &lt;id property="classid" column="classid"/&gt;<br>    &lt;result property="classname" column="classname"/&gt;<br>    &lt;collection property="persons" ofType="person" column="classid" select="shopkeeper.mapper.personMapper.queryPersonByClassId" &gt;<br><br>    &lt;/collection&gt;<br>&lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">select中引用的查询语句：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByClassId" parameterType="int" resultType="person"&gt;<br>    select * from person where class= #{class}<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">这里可以发现column中传入的是外键名，classid在personClass表中的名称是classid，但是在person表中的名称只是class。原因是在resultMap中传入参数给延时加载的sql的。</p>



<p class="is-style-iw-2em">测试类：</p>



<pre class="wp-block-preformatted">public static void queryClassAndPersonsWithLazyloading() throws IOException {
    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");
    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//可以通过build的第二参数指定数据库环境
    </em>SqlSession sqlSession=sqlSessionFactory.openSession();

    personClassMapper personClassMapper =sqlSession.getMapper(personClassMapper.class);


    List&lt;PersonClass&gt; classes=personClassMapper.queryClassAndPersonsWithLazyloading();

    for (PersonClass personClass:classes)
    {
        System.<em>out</em>.println(personClass.getClassid()+", "+personClass.getClassname());
    }

    for (PersonClass personClass:classes)
    {

        List&lt;Person&gt; persons=personClass.getPersons();

        System.<em>out</em>.println(persons);
    }

    sqlSession.close();
    reader.close();
}</pre>



<p class="is-style-iw-2em">日志输出和一对一的差不多。但是这里是使用了personClassMapper中的查询语句，所以要先实现一个接口。</p>



<h2 class="wp-block-heading">查询缓存</h2>



<h3 class="wp-block-heading">一级缓存</h3>



<p class="is-style-iw-2em">作用范围是同一个sqlsession对象。</p>



<p class="is-style-iw-2em">MyBatis默认开启一级缓存，当某个查询第一次执行时，会从DB中查询，结果缓存在sqlsession中。之后这一个sqlsession执行同一个语句的所有查询都会直接去sqlSession中取出结果（不会重新去数据库中再进行一次查询）。</p>



<p class="is-style-iw-2em">每当进行一次commit之后sqlSession就会自动清空所有缓存的结果。</p>



<h3 class="wp-block-heading">二级缓存</h3>



<p class="is-style-iw-2em">MyBatis自带二级缓存，也有第三方提供的二级缓存。</p>



<p class="is-style-iw-2em">Mybatis自带二级缓存， 作用范围是同一个namespace生成的mapper对象。默认不打开，需要手动打开：</p>



<p class="is-style-iw-2em">第一步在conf中开启：</p>



<pre class="wp-block-preformatted">&lt;settings&gt;
<em>...</em>
<em>&lt;!--        开启二级缓存--&gt;
        </em>&lt;setting name="cacheEnabled" value="true"/&gt;
    &lt;/settings&gt;</pre>



<p class="is-style-iw-2em">二级缓存就算打开了，也不是所有的Mapper都会使用；如果要使用二级缓存，还需要再Mapper文件中声明开启二级缓存，实现方式是加一个&lt;cache/&gt;标签。</p>



<p class="is-style-iw-2em">此时如果直接运行测试类，会报以下错错误：</p>



<pre class="wp-block-code"><code>Exception in thread "main" org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: shopkeeper.entity.Person</code></pre>



<p class="is-style-iw-2em">说明二级缓存是在硬盘中进行的，准备缓存的对象必须实现了序列化接口。需要序列化的对象就是开启了二级缓存的mapper对应的对象（personMapper-&gt;person）。这同时也说明了为什么二级缓存的使用需要在每一个Mapper文件中要手动添加一个cache标签：向磁盘io操作很占用系统资源且实体类一般在创建时也不会实现Serializable接口。</p>



<p class="is-style-iw-2em">此外由可序列化的知识可知：级联和父类都要序列化。</p>



<p class="is-style-iw-2em">namespace的值就是接口的全类名(包名.类名)，通过接口可以产生代理对象（personMapper等）；而namespace决定了studentMapper对象的产生所以只要产生的Mapper对象来自于同一个namespace，则这些对象共享二级缓存。</p>



<p class="is-style-iw-2em">如果是同一个SqlSession对象进行多次相同的查询，则直接进入一级缓存查询；当一个SqlSession被close时，就会触发将 一级缓存的内容存入到二级缓存中（貌似commit也会触发，存疑）；如果不是同一个SqlSession对象进行多次相同的查询(但是均来自同一个namespace),则进入二级缓存查询。</p>



<p>测试类方法：</p>



<pre class="wp-block-preformatted">public static void queryClassAndPersonsWithLazyloading() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personClassMapper personClassMapper =sqlSession.getMapper(personClassMapper.class);<br><br><br>    List&lt;PersonClass&gt; classes=personClassMapper.queryClassAndPersonsWithLazyloading();<br><br>    for (PersonClass personClass:classes)<br>    {<br>        System.<em>out</em>.println(personClass.getClassid()+", "+personClass.getClassname());<br>    }<br><br>    for (PersonClass personClass:classes)<br>    {<br><br>        List&lt;Person&gt; persons=personClass.getPersons();<br><br>        System.<em>out</em>.println(persons);<br>    }<br><br>    sqlSession.close();<br><br>    SqlSession sqlSession2=sqlSessionFactory.openSession();<br><br>    personClassMapper personClassMapper2 =sqlSession2.getMapper(personClassMapper.class);<br><br><br>    List&lt;PersonClass&gt; classes2=personClassMapper2.queryClassAndPersonsWithLazyloading();<br><br>    for (PersonClass personClass:classes2)<br>    {<br>        System.<em>out</em>.println(personClass.getClassid()+", "+personClass.getClassname());<br>    }<br><br>    for (PersonClass personClass:classes2)<br>    {<br><br>        List&lt;Person&gt; persons=personClass.getPersons();<br><br>        System.<em>out</em>.println(persons);<br>    }<br><br>    sqlSession2.close();<br>    reader.close();<br>}</pre>



<p class="is-style-iw-2em">运行测试类的日志如下：</p>



<pre class="wp-block-code"><code>DEBUG &#091;main] - ==&gt;  Preparing: select c.* from personclass c
DEBUG &#091;main] - ==&gt; Parameters: 
DEBUG &#091;main] - &lt;==      Total: 2
2, c2
6, c6
DEBUG &#091;main] - Cache Hit Ratio &#091;shopkeeper.mapper.personMapper]: 0.0
DEBUG &#091;main] - ==&gt;  Preparing: select * from person where class= ?
DEBUG &#091;main] - ==&gt; Parameters: 2(Integer)
DEBUG &#091;main] - &lt;==      Total: 1
&#091;Person{age=20, name='陈曦', id=2}]
DEBUG &#091;main] - Cache Hit Ratio &#091;shopkeeper.mapper.personMapper]: 0.0
DEBUG &#091;main] - ==&gt;  Preparing: select * from person where class= ?
DEBUG &#091;main] - ==&gt; Parameters: 6(Integer)
DEBUG &#091;main] - &lt;==      Total: 3
&#091;Person{age=19, name='刘茜元', id=1}, Person{age=19, name='刘闹闹', id=3}, Person{age=19, name='刘咯咯', id=13}]
DEBUG &#091;main] - Resetting autocommit to true on JDBC Connection &#091;com.mysql.cj.jdbc.ConnectionImpl@3eb738bb]
DEBUG &#091;main] - Closing JDBC Connection &#091;com.mysql.cj.jdbc.ConnectionImpl@3eb738bb]
DEBUG &#091;main] - Returned connection 1052195003 to pool.
DEBUG &#091;main] - Cache Hit Ratio &#091;shopkeeper.mapper.personClassMapper]: 0.5
2, c2
6, c6
&#091;Person{age=20, name='陈曦', id=2}]
&#091;Person{age=19, name='刘茜元', id=1}, Person{age=19, name='刘闹闹', id=3}, Person{age=19, name='刘咯咯', id=13}]
</code></pre>



<p class="is-style-iw-2em">可以看见当第一次查询时，命中率为0，第二次查询时命中率为0.5，说明第二次用到时二级缓存中的结果。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3111/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3111</post-id>	</item>
		<item>
		<title>MyBatis学习总结（七）</title>
		<link>https://www.cztcode.com/2020/3109/</link>
					<comments>https://www.cztcode.com/2020/3109/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sun, 12 Jul 2020 15:13:26 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3109</guid>

					<description><![CDATA[MyBatis学习总结（七） 关联查询]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">关联查询</h2>



<h3 class="wp-block-heading">一对一映射</h3>



<p class="is-style-iw-2em">先创建第二张表personidcard和其实体类，然后再给表person添加外码：</p>



<pre class="wp-block-code"><code>alter table person add constraint fk_person_idcard_cardid foreign key (cardid) references personidcard(cardid);</code></pre>



<p class="is-style-iw-2em">我们需要根据传入的id值来查询一个人的全部信息（person表和personidcard表），sql语句是：</p>



<pre class="wp-block-preformatted">select  p.*,c.*<br>from person p inner join personidcard c on p.cardid =c.cardid<br>where p.id=#{id}</pre>



<p class="is-style-iw-2em">但是返回值现在不好处理了，因为这涉及到了两个类中的属性。MyBatis有业务拓展类和resultMap类两种实现方式。</p>



<h4 class="wp-block-heading">业务拓展类</h4>



<p class="is-style-iw-2em">需要新建一个业务拓展类，使其继承成员变量多的类（person），然后直接代码创建变量少的类的属性：</p>



<pre class="wp-block-preformatted">public class PersonBussiness extends Person{<br>    private int cardid;<br>    private String cardinfo;<br><br>    public int getCardid() {<br>        return cardid;<br>    }<br><br>    public void setCardid(int cardid) {<br>        this.cardid = cardid;<br>    }<br><br>    public String getCardinfo() {<br>        return cardinfo;<br>    }<br><br>    public void setCardinfo(String cardinfo) {<br>        this.cardinfo = cardinfo;<br>    }<br><br>    @Override<br>    public String toString() {<br>        return super.toString()+", "+this.cardid+", "+this.cardinfo;<br>    }<br>}</pre>



<p class="is-style-iw-2em">这样就可以在resultType中写入返回值类型是PersonBussiness了：</p>



<pre class="wp-block-preformatted">&lt;select id="queryStudentWithBussiness" parameterType="int" resultType="personbussiness"&gt;<br>    select  p.*,c.*<br>    from person p inner join personidcard c on p.cardid =c.cardid<br>    where p.id=#{id}<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">用业务拓展类的核心是用resultType指定类的属性包含多表查询的所有字段。但是这个方法有些弊端，如果有很多张表都有关联，业务拓展类实现起来就很麻烦了。</p>



<h4 class="wp-block-heading">ResultMap</h4>



<p class="is-style-iw-2em">之前已经多次出现这个resultMap了，给人的感觉是，实现简单的东西比较繁杂，但是可以去做一些特定的任务，给人一种吃力不讨好的感觉。果不其然用resultMap来实现关联查询就十分的方便。</p>



<p class="is-style-iw-2em">在数据库中通过外键将两个表关联起来，那么在java中将两个类关联起来可以通过将一个类作为另外一个类的成员变量来实现（在person类中增加成员PersonIdCard）。</p>



<p class="is-style-iw-2em">将PersonIdCard类关联到person类中之后resultMap的返回值就可以直接写Person了：</p>



<pre class="wp-block-preformatted">&lt;select id="queryStudentWithHashMapForFk" parameterType="int" resultMap="person_idCard_map"&gt;<br>        select  p.*,c.*<br>        from person p inner join personidcard c on p.cardid =c.cardid<br>        where p.id=#{id}<br>    &lt;/select&gt;<br>    <br>    &lt;resultMap id="person_idCard_map" type="Person"&gt;<br>        &lt;id property="id" column="id"/&gt;<br>        &lt;result property="name" column="name"/&gt;<br>        &lt;result property="age" column="age"/&gt;<br><em>&lt;!--        </em><em>对象成员时，如果是一对一映射，就用</em><em>association;javaType</em><em>指定改属性的类型</em><em>--&gt;<br></em><em>        </em>&lt;association property="personIdCard" javaType="PersonIdCard"&gt;<br>            &lt;id property="cardid" column="cardid"/&gt;<br>            &lt;result property="cardinfo" column="cardinfo"/&gt;<br>        &lt;/association&gt;<br>    &lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">resultMap中的id和result字段和之前的处理方式一样，现在主要针对于对象类型，即关联的personidcard类的处理。如果是一对一映射就用association标签；如果是一对多就是collection标签。在association标签中还需要加上这个成员变量的名称property和类型javaType。</p>



<p class="is-style-iw-2em">这个关联类中也有自己的成员变量，其本身也对应了一张表，所以也要写上自身的变量，同样是用id和result。</p>



<h3 class="wp-block-heading">一对多映射</h3>



<p class="is-style-iw-2em">新建另一张表personClass和其实体类，给person添加外码class。然后说一个题外话：<a href="http://www.dongchuanmin.com/archives/550.html" class="rank-math-link" target="_blank" rel="noopener">Mysql不允许同时更新（删除）和查询同一张表</a>，是我在尝试以多此一举的方式更新表的时候发现的这个问题。</p>



<p class="is-style-iw-2em">由于还是用ResultMap来实现，所以要将personClass和person这两个类关联起来。现在是一对多的联系，只能在personClass类中增加person的list。</p>



<p class="is-style-iw-2em">在resultMap中，其它的写法全都一样，唯一不同的是，现在关联的类是一对多关联，要写collection（一对一写association），并且描述类型的时候不能再用javatype了（因为javaType不用写list，而单写一个person不能正确表示personClass当中的成员的类型，也不能体现其是一对多关联），而是特有的ofType，然后ofType里面写person，表明这个集合的元素类型是person：</p>



<pre class="wp-block-preformatted">&lt;select id="queryClassAndPersons" parameterType="int" resultMap="class_person_map"&gt;<em>
        </em>select c.*,p.*
        from person p
        inner join personclass c on c.classid=p.class
        where p.class=#{class}
    &lt;/select&gt;
<em>
    </em>&lt;resultMap id="class_person_map" type="personclass"&gt;
        &lt;id property="classid" column="classid"/&gt;
        &lt;result property="classname" column="classname"/&gt;
<em>&lt;!-- 描述属性类型javaType
 描述属性的元素类型是oftype--&gt;
        </em>&lt;collection property="persons" ofType="person"&gt;
            &lt;id property="id" column="id"/&gt;
            &lt;result property="name" column="name"/&gt;
            &lt;result property="age" column="age"/&gt;

        &lt;/collection&gt;
    &lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">然后collection标签内依旧要配置person的成员属性。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3109/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3109</post-id>	</item>
		<item>
		<title>MyBati学习总结（六）</title>
		<link>https://www.cztcode.com/2020/3102/</link>
					<comments>https://www.cztcode.com/2020/3102/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sun, 12 Jul 2020 13:56:53 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3102</guid>

					<description><![CDATA[MyBati学习总结（六） 动态SQL]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">动态SQL</h2>



<p class="is-style-iw-2em">动态SQL能够根据输入的参数而确定最终提交给数据库的SQL语句。当传入的参数满足一定的条件时，MyBatis就会相应地拼接对应的语句，且能够自动地处理空格、逗号和语句头部的连词。</p>



<p class="is-style-iw-2em">动态SQL需要用到if、where、foreach等标签来实现。</p>



<h3 class="wp-block-heading">if标签</h3>



<p class="is-style-iw-2em">if标签时动态SQL的灵魂，分支条件全都要if标签的test属性来实现。如果test当中的语句结果为真，就将这个if标签中的sql语句拼接到总的sql语句中：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person"&gt;
        select id,name,age from person where <em>
        </em>&lt;if test="name!=null and name!=''"&gt;
            name=#{name}
        &lt;/if&gt;
        &lt;if test="age!=null and age!=0"&gt;
            and age=#{age}
        &lt;/if&gt;

    &lt;/select&gt;</pre>



<pre class="wp-block-code"><code>Person person=new Person();
        person.setName("刘咯咯");
        person.setAge(19);
        Person pers=personMapper.queryPerosnByNameOrAgeWithSQLTag(person);</code></pre>



<h3 class="wp-block-heading">where标签</h3>



<p class="is-style-iw-2em">if标签是按照代码的顺序执行的，如果第一个条件不成立，且有第二个条件成立的话，由于第二个if标签中的sql语句首部必然有连接词and或or，那么将其与原sql语句拼接之后必然会导致sql语句解析错误。</p>



<p class="is-style-iw-2em">可以巧妙地在where后面加一句1=1，就可以暂时解决这个问题了：</p>



<pre class="wp-block-preformatted">    &lt;select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person"&gt;
        select id,name,age from person where 1=1<em>
        </em>&lt;if test="name!=null and name!=''"&gt;
            and name=#{name}
        &lt;/if&gt;
        &lt;if test="age!=null and age!=0"&gt;
            and age=#{age}
        &lt;/if&gt;

    &lt;/select&gt;</pre>



<p class="is-style-iw-2em">但是还是有一个问题，1=1对于or来讲就是恒真了，会屏蔽所有or后i按的语句，你说加一句1&lt;&gt;1吧，又防不了and语句，所以MyBatis推出了更加灵活的where标签来处理这问题：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person"&gt;<br>    select id,name,age from person<br>    &lt;where&gt;<br>        &lt;if test="name!=null and name!=''"&gt;<br>            and name=#{name}<br>        &lt;/if&gt;<br>        &lt;if test="age!=null and age!=0"&gt;<br>            and age=#{age}<br>        &lt;/if&gt;<br>    &lt;/where&gt;<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">将所有的if标签都写入where标签内，where语句会处理第一个成立的if标签中的and（或者or）。这个时候哪怕第一个if中写上了and或test不通过，也能够拼接成正确的语句。不过where只会处理第一个成立的if标签，如果之后的标签中缺少连接词的话研究会报错。</p>



<h3 class="wp-block-heading">foreach标签</h3>



<p class="is-style-iw-2em">foreach标签是为了让动态SQL能够处理in语句中的内容，例如查询编号为1、2、13的人的信息：</p>



<pre class="wp-block-code"><code>select id, name from person where id in(1,2,13)</code></pre>



<p class="is-style-iw-2em">我们想让in后面的那个集合能用动态SQL的方式动态传入。显然多个元素的话需要遍历每一个元素来查询，如果企图直接通过读取属性的方式是行不通的，例如定义一个Country类，其中有一个成员变量list为ids，在下面的语句中，会报无法识别ids的错误：</p>



<pre class="wp-block-code"><code>&lt;select id="queryPersonWithIdInCountry" parameterType="Country" resultType="person"&gt;
        select * from person
        &lt;where&gt;
            &lt;if test="ids!=null and ids.size&gt;0"&gt;
                and id in(ids)
            &lt;/if&gt;
        &lt;/where&gt;

&lt;/select&gt;</code></pre>



<p class="is-style-iw-2em">foreach看名字就知道是实现迭代的功能。foreach可以迭代的类型有：属性、数组（包括对象数组）、集合等。</p>



<h4 class="wp-block-heading">属性</h4>



<p class="is-style-iw-2em">还是以上面的那个country中的属性ids为例，想要用foreach处理它，实现起来就和java中的增强for差不多：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonWithIdInCountry" parameterType="Country" resultType="person"&gt;<br>    select <em>* </em>from person<br>    &lt;where&gt;<br>        &lt;if test="ids!=null and ids.size&gt;0"&gt;<br>        &lt;foreach collection="ids" open=" and id in(" close=")" item="id" separator=","&gt;<br>            #{id}<br>        &lt;/foreach&gt;<br><br>        &lt;/if&gt;<br>    &lt;/where&gt;<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">collection中写属性名，item中写入每一次遍历取出的值（相当于for (String name: [names])中的name）；由于一个sql语句中不止要遍历的集合，还包括语句中的其它部分，所以用open写入要迭代的元素的前面的内容，用close写入后面的内容。最后还需要定义分隔符，用separator来写。</p>



<p class="is-style-iw-2em">foreach标签内写入展位符，表示要迭代的部分的取值。所以整个sql语句就被拆成了这样：</p>



<pre class="wp-block-preformatted">select <em>* </em>from person【open】and id in(【item】#{id}【separator】，【item】#{id}【close】）</pre>



<h4 class="wp-block-heading">简单类型的数组</h4>



<p class="is-style-iw-2em">当传入的是简单类型的数组时，不仅输入参数的类型要改为对应的数组类型，而且在使用时需要用array来代替所有的数组名。原因时数组名时作为形参传入进来的，无法写成映射。</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonWithArray" parameterType="int[]" resultType="person"&gt;<br>    select <em>* </em>from person<br>    &lt;where&gt;<br>        &lt;if test="array!=null and array.length&gt;0"&gt;<br>            &lt;foreach collection="array" open=" and id in(" close=")" item="id" separator=","&gt;<br>                #{id}<br>            &lt;/foreach&gt;<br>        &lt;/if&gt;<br>    &lt;/where&gt;<br>&lt;/select&gt;</pre>



<h4 class="wp-block-heading">集合类型（list）</h4>



<p class="is-style-iw-2em">和传入简单类型的数组差不多，将传入的参数写为list，且将所有集合名写为list：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonWithList" parameterType="list" resultType="person"&gt;<br>    select <em>* </em>from person<br>    &lt;where&gt;<br>        &lt;if test="list!=null and list.size&gt;0"&gt;<br>            &lt;foreach collection="list" open=" and id in(" close=")" item="id" separator=","&gt;<br>                #{id}<br>            &lt;/foreach&gt;<br><br>        &lt;/if&gt;<br>    &lt;/where&gt;<br>&lt;/select&gt;</pre>



<h4 class="wp-block-heading">对象数组</h4>



<p class="is-style-iw-2em">假设要传入一个person类的数组：</p>



<pre class="wp-block-preformatted">Person per1=new Person();<br>per1.setId(1);<br>Person per2=new Person();<br>per2.setId(2);<br>Person per13=new Person();<br>per13.setId(13);<br><br>Person persons[]=new Person[]{per1,per2,per13};</pre>



<p class="is-style-iw-2em">现在传入的类型并不能写Person[]了，会报错无法识别这个别名，只能传入Object[]（我觉得挺迷的，为什么parameterType写单个Person类就可以写Person数组就不行了）。</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonWithObjectArray" parameterType="Object[]" resultType="person"&gt;
    select <em>* </em>from person
    &lt;where&gt;
      &lt;if test="array!=null and array.length&gt;0"&gt;
          &lt;foreach collection="array" open=" and id in(" close=")" item="person" separator=","&gt;
             #{person.id}
          &lt;/foreach&gt;
      &lt;/if&gt;
    &lt;/where&gt;
&lt;/select&gt;</pre>



<p class="is-style-iw-2em">现在每一次迭代取出的都是一个person实例，真正要用的其实时person实例中的id变量，所以在占位符中要写item中的临时变量（person）点id。</p>



<h3 class="wp-block-heading">SQL片段</h3>



<p class="is-style-iw-2em">对于需要重复出现很多次的代码，在java中可以将其写入一个方法中，在SQL里可以写入存储过程或存储函数中。那么在MyBatis中，可以使用SQL片段处理可能需要多次出现的SQL语句。</p>



<p class="is-style-iw-2em"><strong>第一步要提取相似代码。 </strong>在Mapper.xml文件中的namespace标签下写sql标签（与增删改查的标签同级），在sql标签中写入需要提取的代码：</p>



<pre class="wp-block-preformatted">&lt;sql id="queryWithObjectArray"&gt;<br>    &lt;where&gt;<br>        &lt;if test="array!=null and array.length&gt;0"&gt;<br>            &lt;foreach collection="array" open=" and id in(" close=")" item="person" separator=","&gt;<br>                #{person.id}<br>            &lt;/foreach&gt;<br>        &lt;/if&gt;<br>    &lt;/where&gt;<br>&lt;/sql&gt;</pre>



<p class="is-style-iw-2em">sql标签的id属性作用与增删改查的id相同，唯一标识这个SQL片段。</p>



<p class="is-style-iw-2em"><strong>第二步引用。</strong>在需要引用的地方写入refield标签，补充完id值之后就可以引用完成了：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonWithObjectArray" parameterType="Object[]" resultType="person"&gt;<br>    select <em>* </em>from person<br>    &lt;include refid="queryWithObjectArray"&gt;&lt;/include&gt;<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">如果引用的地方不在SQL片段同一个文件，需要在引用时加上namespace。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3102/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3102</post-id>	</item>
		<item>
		<title>MyBatis学习总结（五）</title>
		<link>https://www.cztcode.com/2020/3079/</link>
					<comments>https://www.cztcode.com/2020/3079/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 11 Jul 2020 13:24:03 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3079</guid>

					<description><![CDATA[MyBatis学习总结（五） 详解ResultType]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">详解ResultType</h2>



<h3 class="wp-block-heading">简单类型</h3>



<p class="is-style-iw-2em">八个基本类型+String。举例：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonCount" resultType="int"&gt;<br>    select <em>count</em>(<em>*</em>) from person<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">int queryPersonCount();</pre>



<h3 class="wp-block-heading">输出参数为实体对象类型</h3>



<p class="is-style-iw-2em">举例：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonById" resultType="person" parameterType="int"&gt;
    select <em>* </em>from person where id=#{id}
&lt;/select&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">Person queryPersonById(int id);</pre>



<h3 class="wp-block-heading">输出参数为实体对象类型的集合</h3>



<p class="is-style-iw-2em">有多个查询结果的情况。举例：</p>



<pre class="wp-block-preformatted">&lt;select id="queryAll" resultType="person"&gt;<br>    select <em>* </em>from person<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">List&lt;Person&gt; queryAll();</pre>



<p class="is-style-iw-2em">虽然输出类型为集合，但是resultType依然写集合的元素类型(resyltType=&#8221;person&#8221;)，接口中的方法返回值要写集合类型list。</p>



<h3 class="wp-block-heading">输出参数类型是HashMap</h3>



<p class="is-style-iw-2em">若返回值为HashMap，就需要有键值对。MyBatis实现的方式是在sql语句中给要查询的列名起一个别名，让别名作为key，查询的结果作为对应的value。举例：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonOutByHashMap" resultType="HashMap"&gt;<br>    select id "mId", name "mName" from person where id=1<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">HashMap&lt;String,Object&gt; queryPersonOutByHashMap();</pre>



<p class="is-style-iw-2em"><strong>测试类中获取结果</strong>：</p>



<pre class="wp-block-preformatted"><em>//查询人，返回值为HashMap
</em>public static void queryPersonOutByHashMap() throws IOException {
    ...
    HashMap&lt;String,Object&gt; personMap=personMapper.queryPersonOutByHashMap();

    System.<em>out</em>.println(personMap);
    ...
}</pre>



<p class="is-style-iw-2em">以上是查询只有一个返回的结果，如果有多个结果，那么xml文件中的resultType依旧是HashMap，但是接口方法需要用list（同第三种）。举例：</p>



<pre class="wp-block-preformatted">&lt;select id="queryAllPersonOutByHashMap" resultType="HashMap"&gt;<br>    select id "mId", name "mName" from person<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">List&lt;HashMap&lt;String,Object&gt;&gt; queryAllPersonOutByHashMap();</pre>



<p class="is-style-iw-2em"><strong>测试类中获取结果：</strong></p>



<pre class="wp-block-preformatted"><em>//查询人，返回值为HashMap的集合
</em>public static void queryAllPersonOutByHashMap() throws IOException {
    ...
    List&lt;HashMap&lt;String,Object&gt;&gt; personMaps=personMapper.queryAllPersonOutByHashMap();

    System.<em>out</em>.println(personMaps);
    ...
}</pre>



<h3 class="wp-block-heading">当属性名和字段名不一致时</h3>



<p class="is-style-iw-2em">之前是使用ressultMap来实现的：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByIdWithConverter" resultMap="resultPerson" parameterType="int"&gt;
    select <em>* </em>from person where id=#{id}
&lt;/select&gt;
&lt;resultMap id="resultPerson" type="person"&gt;<em>
    </em><strong>&lt;id property="num" column="id"/&gt;</strong>
    &lt;result property="name"  column="name"/&gt;
    &lt;result property="age" column="age"/&gt;
    &lt;result property="sex" column="sex" javaType="boolean" jdbcType="INTEGER"/&gt;
&lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em"><strong>接口方法：</strong></p>



<pre class="wp-block-preformatted">Person queryPersonByIdWithConverter(int id);</pre>



<p class="is-style-iw-2em">实际上还可以用resultType+HashMap的方式解决（前提是将实体类person中的id和name变量暂时改为mId和mName：</p>



<pre class="wp-block-code"><code>&lt;select id="queryAllPersonOutByHashMap" resultType="person"&gt;
        select id "mId", name "mName" from person
&lt;/select&gt;
</code></pre>



<p class="is-style-iw-2em">这么做的原理是，MyBatis根据sql语句中起的别名，将别名自动与person进行映射（原先是将实体类的属性和列名映射）。相当于MyBatis在自动映射时多了应该选择。</p>



<p class="is-style-iw-2em">上面的当返回值时为HashMap的情况也用到了别名，但是这个别名是MyBatis拿过来作为HashMap的key值的，实现的道理不一样。</p>



<p class="is-style-iw-2em">我们可以发现当属性名和字段名不一致时，使用resultType+HashMap比使用resultMap要简洁很多。我认为一般写resultMap主要是为了使用类型转换器。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3079/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3079</post-id>	</item>
		<item>
		<title>MyBatis学习总结（四）</title>
		<link>https://www.cztcode.com/2020/3058/</link>
					<comments>https://www.cztcode.com/2020/3058/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 11 Jul 2020 06:49:38 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3058</guid>

					<description><![CDATA[MyBatis学习总结（四） 详解parameterType]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">详解parameterType</h2>



<p class="is-style-iw-2em">Mapper.xml的每一个sql标签的parameterType属性表示输入参数，并且在sql语句中，用#{}或${}来从parameterType中取得对应的值。当输入参数的类型不同时，#{}和${}的用法有很大的区别。</p>



<h3 class="wp-block-heading">基本类型</h3>



<p class="is-style-iw-2em">当输入参数时八个基本类型或string类型时，#{}和${}既有相似之处，又有不同的用法：</p>



<h4 class="wp-block-heading">不同</h4>



<p class="is-style-iw-2em"><strong>第一、</strong>基本类型中#{}中的内容可以写任意值，如# {xxx}；而${}中必须写value，如$ {value}（但是在新的jar包中取消了这一限制，所以这一点没有什么区别了）。</p>



<p class="is-style-iw-2em"><strong>第二、</strong>对于String类型，#{}会自动给里面的参数套上单引号&#8217; &#8216;，然而${}会进行原样输出，那么以下这个语句：</p>



<pre class="wp-block-code"><code>&lt;select id="queryPersonByName" resultType="person" parameterType="string"&gt;
    select * from person where name= ${values} 
&lt;/select&gt;</code></pre>



<p class="is-style-iw-2em">实际上提交给数据库的是</p>



<pre class="wp-block-preformatted">select * from person where name= 刘咯咯 ;</pre>



<p class="is-style-iw-2em">这个语句显然在sql中是不成立的，原因是数据库的字符串要加上单引号。需要改写成以下的样子：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByName" resultType="person" parameterType="string"&gt;
    select <em>* </em>from person where name='${value}'
&lt;/select&gt;</pre>



<p class="is-style-iw-2em">但是${}在动态排序里面就有很大的作用。一个根据输入的列名的排序语句如果用#{}写是这样的：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonOrderedByColumn" resultType="person" parameterType="string"&gt;<br>    select <em>* </em>from person order by ${column } desc<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">将列名带入后实际上的sql语句是：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonOrderedByColumn" resultType="person" parameterType="string"&gt;
    select <em>* </em>from person order by 'name' desc
&lt;/select&gt;</pre>



<p class="is-style-iw-2em">&#8216;name&#8217;是一个常量，比较无结果，所以这个时候不能用#{}自动生成单引号。</p>



<p class="is-style-iw-2em"><strong>第三、</strong>#{}可以防止SQL注入而${}不防止。sql注入是什么我现在也不知道，等以后要用到再说吧，<a href="https://www.cnblogs.com/myseries/p/10821372.html" class="rank-math-link" target="_blank" rel="noopener">参考资料</a>。</p>



<h4 class="wp-block-heading">相同</h4>



<p class="is-style-iw-2em">都可以获取对象的值(嵌套类型对象)</p>



<p class="is-style-iw-2em"><strong>第一、</strong>在模糊查询中，这两种方式虽然写法稍有不同，但是都能实现：</p>



<p class="is-style-iw-2em"><strong>#{}</strong></p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByNameOrAge" resultType="person" parameterType="person"&gt;
    select <em>* </em>from person where name like #{name} or age=#{age}
&lt;/select&gt;

Person person=new Person();
person.setAge(19);
person.setName("%刘%");</pre>



<p class="is-style-iw-2em"><strong>${}</strong></p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByNameOrAge" resultType="person" parameterType="person"&gt;
    select <em>* </em>from person where name like '%${name}%' or age=#{age}
&lt;/select&gt;

Person person=new Person();
person.setAge(19);
person.setName("刘");</pre>



<p class="is-style-iw-2em"><strong>第二、</strong>获取对象的值的方式相同</p>



<p class="is-style-iw-2em">原来直接传入的是person类的实例，现在新建一个类address，根据address的值来查询。可以直接将address作为输入参数传入：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByAddress" resultType="person" parameterType="address"&gt;<br>    select <em>* </em>from person where homeaddress=#{homeAddress} or schooladdress='${schoolAddress}'<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">也可以直接获取嵌套对象的值（传入person获取其address）：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByAddress" resultType="person" parameterType="person"&gt;<br>    select <em>* </em>from person where homeaddress=#{address.homeAddress} or schooladdress='${address.schoolAddress}'<br>&lt;/select&gt;</pre>



<h3 class="wp-block-heading">对象类型</h3>



<p class="is-style-iw-2em">当parameterType的取值是对象类型时，使用#{}和${}都必须写属性名：</p>



<pre class="wp-block-code"><code>&lt;select id="queryPersonByNameOrAge" resultType="person" parameterType="person"&gt;
        select * from person where name like '%${name}%' or age=#{age}
    &lt;/select&gt;</code></pre>



<p class="is-style-iw-2em">如果输入的值为多个，还可以用HashMap来作为parameterType的取值：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByNameOrAgeWithHashMap" resultType="person" parameterType="HashMap"&gt;
    select <em>* </em>from person where name like '%${mName}%' or age=#{mAge}
&lt;/select&gt;</pre>



<p class="is-style-iw-2em">传入的HashMap，key的值去匹配占位符，如果名称相同就用value去替换掉占位符：</p>



<pre class="wp-block-preformatted">Map&lt;String,Object&gt; personMap=new HashMap&lt;&gt;();<br>personMap.put("mName","刘");<br>personMap.put("mAge",20);<br>List&lt;Person&gt; persons=personMapper.queryPersonByNameOrAgeWithHashMap(personMap);</pre>



<p class="is-style-iw-2em">parameterType用HashMap来作为输入参数的一个重要用途是调用存储过程。这一点是仅仅传入一个实体类对象是无法实现的。</p>



<h3 class="wp-block-heading">parameterType为HashMap时调用存储过程</h3>



<p class="is-style-iw-2em">存储过程无论输入参数是什么值，语法上都需要用map来传递该值.</p>



<p class="is-style-iw-2em">首先在数据库中先创建两个存储过程：</p>



<p class="is-style-iw-2em"><strong>1.查询某个年龄的总人数</strong></p>



<pre class="wp-block-code"><code>DELIMITER //
create PROCEDURE queryCountByAgeWithProcedure (IN mAge int, OUT pNum int)
begin
select count(1) into pNum from person where age=mAge;
end
//
DELIMITER ;</code></pre>



<p class="is-style-iw-2em"><strong>2.根据id值来删除一个人的信息</strong></p>



<pre class="wp-block-code"><code>DELIMITER //
Create procedure deletePersonByIdWithProcedure(In mId int)
Begin 
Delete from person where id =mId;
End
//
DELIMITER ;</code></pre>



<p class="is-style-iw-2em">mysql中创建存储过程要先将分隔符改成别的东西。<a href="https://www.cnblogs.com/qk2014/p/9456118.html" class="rank-math-link" target="_blank" rel="noopener">参考资料</a></p>



<h4 class="wp-block-heading">Mapper.xml</h4>



<p class="is-style-iw-2em">除了parameterType为HashMap之外，还需要写上一个statementType=CALLABLE以表示这个语句是执行的存储过程。之前所有的sql标签都没有写是因为statementType默认值是PREPARED；此外statementType还有一个类型是STATEMENT，表示直接操作sql，不进行预编译：</p>



<pre class="wp-block-preformatted">&lt;select id="queryCountByAgeWithProcedure" statementType="CALLABLE" parameterType="HashMap"&gt;
    ...
&lt;/select&gt;</pre>



<p class="is-style-iw-2em">通过关键字CALL来调取存储过程，调用的存储过程还需要写明参数的名称、类型、模式，CALL需要在大括号中调用：</p>



<pre class="wp-block-preformatted">&lt;select id="queryCountByAgeWithProcedure" statementType="CALLABLE" parameterType="HashMap"&gt;<br>    {<br>        CALL <em>queryCountByAgeWithProcedure</em>(<br>            #{mAge,jdbcType=INTEGER,mode=IN},<br>            #{pNum,jdbcType=INTEGER,mode=OUT}<br>        )<br>    }<br>&lt;/select&gt;</pre>



<h4 class="wp-block-heading">测试类方法</h4>



<p class="is-style-iw-2em">在测试类中，输入的参数还是和上面的一样用HashMap的put方法，同样也是要让key值和占位符匹配。不过在MyBatis中所有的存储过程的方法都没有返回值——返回值存储在HashMap中，所以接口中的方法返回值是void类型:</p>



<pre class="wp-block-preformatted">public interface personMapper {

    <em>...</em>
<em>
    </em>void queryCountByAgeWithProcedure(Map&lt;String,Object&gt; para);
    void deletePersonByIdWithProcedure(Map&lt;String,Object&gt;para);

}</pre>



<p class="is-style-iw-2em">从HashMap中取出存储过程的返回值的方式：</p>



<pre class="wp-block-preformatted"><em>//用存储过程来查询年龄总人数
</em>public static void queryCountByAgeWithProcedure() throws IOException {
    ...

    Map&lt;String,Object&gt; map=new HashMap&lt;&gt;();
    map.put("mAge",19);
    personMapper.queryCountByAgeWithProcedure(map);

    Object pNum=map.get("pNum");
    System.<em>out</em>.println("查询到"+pNum+"人");
    
    ...
}</pre>



<p class="is-style-iw-2em">存储过程的增删改也都需要在最后commit()一下：</p>



<pre class="wp-block-preformatted"><em>//</em><em>用存储过程来根据</em><em>id</em><em>删人<br></em>public static void deletePersonByIdWithProcedure() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<br>    SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br><br>    Map&lt;String,Object&gt; map=new HashMap&lt;&gt;();<br>    map.put("mId",4);<br>    personMapper.deletePersonByIdWithProcedure(map);<br>    sqlSession.commit();<br><br>    sqlSession.close();<br>    reader.close();<br>}</pre>



<h4 class="wp-block-heading">jdbcType</h4>



<p class="is-style-iw-2em">jdbcType这个东西是MyBatis独有的，对应还有一个是javaType。虽然说java的类型能直接和数据库的类型映射，但是当java的一个变量的值是null时，数据库可能只知道其为空值，但是不知道它的具体类型是什么。</p>



<p class="is-style-iw-2em">例如当parameterType为HashMap的时候，我们定义的HashMap的value类型是Object，此时如果value=null，那么数据库就无法将这个null和自己的类型进行匹配。所以Mybatis在实现可能存在未知类型的空值时要求用上jdbcType，以告诉数据库这是一个什么样的空值。</p>



<p class="is-style-iw-2em">但是一般情况下数据的类型时可以确定的（例如直接写明parameterType的类型），myBatis可以直接将类型告诉给数据库，此时就不是一定要加上jdbcType了。</p>



<p class="is-style-iw-2em"><a href="https://blog.csdn.net/gaokao2011/article/details/88728197" class="rank-math-link" target="_blank" rel="noopener">jdbcType和javaType的对应关系</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3058/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3058</post-id>	</item>
		<item>
		<title>MyBatis学习总结（三）</title>
		<link>https://www.cztcode.com/2020/3053/</link>
					<comments>https://www.cztcode.com/2020/3053/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 09 Jul 2020 14:58:57 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3053</guid>

					<description><![CDATA[MyBatis学习总结（三） 一些优化 全局参数、别名、类型转换器、resultMap]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">一些优化</h2>



<h3 class="wp-block-heading">优化数据库配置信息</h3>



<p class="is-style-iw-2em">可以将配置信息单独放入（要手动新建的）db. properties文件中， 然后再从conf.xml动态引入。</p>



<p class="is-style-iw-2em">db. properties文件内容是由key-value组成的：</p>



<pre class="wp-block-preformatted">driver:com.mysql.cj.jdbc.Driver<br>url:jdbc:mysql://localhost:3306/person?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT<br>username:root<br>password:password</pre>



<p class="is-style-iw-2em">conf引用方式，是在最外层configuration下添加properties标签：</p>



<pre class="wp-block-preformatted">&lt;properties resource="db.properties"/&gt;</pre>



<p class="is-style-iw-2em">有一个小小问题是，最开始的配置信息是直接写在conf.xml文件中的，我将配置信息直接从eclipse的jdbc项目中抄写过来的，idea自动帮我在url的每一个“&amp;”号处添加了“amp；”，但是写在db. properties文件中要删除。</p>



<h3 class="wp-block-heading">MyBatis全局参数</h3>



<p class="is-style-iw-2em">MyBatis全局参数就是一些MyBatis具体运行时候的参数设置，一般要慎用，有以下这些参数：</p>



<figure class="wp-block-table"><table><tbody><tr><td>参数</td><td>简介</td><td>有效值</td></tr><tr><td>cacheEnabled</td><td>在全局范围内，启用或禁用缓存</td><td>true（默认）、false</td></tr><tr><td>lazyLoadingEnabled</td><td>在全局范围内启用或禁用延迟加载。当禁用时，所有相关联的对象都将立即加载（热加载）。</td><td>true（默认）、false</td></tr><tr><td>aggressiveLazyLoading</td><td>启用时，有延迟加载属性的对象，在被调用时将会完全加载所有属性（立即加载）。否则，每一个属性都将按需加载（即延迟加载）。</td><td>true（默认）、false</td></tr><tr><td>multipleResultSetsEnabled</td><td>允许或禁止执行一条单独的SQL语句后返回多条结果（结果集）；需要驱动程序的支持</td><td>true（默认）、false</td></tr><tr><td>autoMappingBehavior</td><td>指定数据表字段和对象属性的映射方式。NONE：禁止自动映射，只允许手工配置的映射PARTIAL：只会自动映射简单的、没有嵌套的结果FULL：自动映射任何结果（包含嵌套等）</td><td>NONE、PARTIAL（默认）、FULL</td></tr><tr><td>defaultExecutorType</td><td>指定默认的执行器。SIMPLE：普通的执行器。REUSE：可以重复使用prepared statements语句。BATCH：可以重复执行语句和批量更新。</td><td>SIMPLE（默认）、REUSE、BATCH</td></tr><tr><td>defaultStatementTimeout</td><td>设置驱动器等待数据库回应的最长时间</td><td>以秒为单位的，任意正整数。无默认值</td></tr><tr><td>safeRowBoundsEnabled</td><td>允许或禁止使用嵌套的语句</td><td>true、false（默认）</td></tr><tr><td>mapUnderscoreToCamelCase</td><td>当在数据表中遇到有下划线的字段时，自动映射到相应驼峰式形式的Java属性名。例如，会自动将数据表中的stu_no字段，映射到POJO类的stuNo属性。</td><td>true、false（默认）</td></tr><tr><td>lazyLoadTriggerMethods</td><td>指定触发延迟加载的对象的方法</td><td>equals、clone、hashCode、toString</td></tr></tbody></table></figure>



<p class="is-style-iw-2em">如果有需要，在conf.xml文件中设置：</p>



<pre class="wp-block-preformatted">&lt;settings&gt;<br>    &lt;setting name="" value=""/&gt;<br>&lt;/settings&gt;</pre>



<p class="is-style-iw-2em">例如：</p>



<pre class="wp-block-code"><code>&lt;settings&gt;
   &lt;setting name= "cacheEnabled" value= "false" /&gt;
   &lt;setting name= "LazyLoadingEnabled" value="false" /&gt;
&lt;/settings&gt;
</code></pre>



<h3 class="wp-block-heading">设置别名</h3>



<p class="is-style-iw-2em">别名就是给一些很长很复杂的值起一个简单的别名。别名是忽略大小写的，有以下两种方式：</p>



<p class="is-style-iw-2em"><strong>设置单个别名</strong>：</p>



<pre class="wp-block-code"><code>    &lt;typeAliases&gt;
       &lt;typeAlias type="shopkeeper.entity.Person" alias="person"/&gt;
    &lt;/typeAliases&gt;</code></pre>



<p class="is-style-iw-2em"><strong>批量设置别名</strong></p>



<pre class="wp-block-code"><code>    &lt;typeAliases&gt;
        &lt;package name="shopkeeper.entity"/&gt;
    &lt;/typeAliases&gt;</code></pre>



<p class="is-style-iw-2em">除了我们可以自定义别名之外，MyBatis还有一些内置的别名：</p>



<figure class="wp-block-image"><img decoding="async" src="https://images2017.cnblogs.com/blog/922732/201710/922732-20171018111714787-1035176644.png" alt="" /></figure>



<figure class="wp-block-image"><img decoding="async" src="https://images2017.cnblogs.com/blog/922732/201710/922732-20171018111746240-664658202.png" alt="" /></figure>



<h3 class="wp-block-heading">类型转换器</h3>



<p class="is-style-iw-2em">java类型和数据库（如MySQL）的类型并不是完全匹配，例如String和varchar，但是MyBatis可以自动将其相互转化，这里就是用到了类型转换器。</p>



<p class="is-style-iw-2em"><strong>MyBatis自带一些常见的类型处理器：</strong></p>



<figure class="wp-block-table"><table><tbody><tr><td><strong>类型处理器</strong></td><td><strong>Java类型</strong></td><td><strong>JDBC类型</strong></td></tr><tr><td>BooleanTypeHandler</td><td>java.lang.Boolean,boolean</td><td>数据库兼容的&nbsp;BOOLEAN</td></tr><tr><td>ByteTypeHandler</td><td>java.lang.Byte,&nbsp;byte</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;BYTE</td></tr><tr><td>ShortTypeHandler</td><td>java.lang.Short,&nbsp;short</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;SHORT INTEGER</td></tr><tr><td>IntegerTypeHandler</td><td>java.lang.Integer,&nbsp;int</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;INTEGER</td></tr><tr><td>LongTypeHandler</td><td>java.lang.Long,&nbsp;long</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;LONG INTEGER</td></tr><tr><td>FloatTypeHandler</td><td>java.lang.Float,&nbsp;float</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;FLOAT</td></tr><tr><td>DoubleTypeHandler</td><td>java.lang.Double,double</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;DOUBLE</td></tr><tr><td>BigDecimalTypeHandler</td><td>java.math.BigDecimal</td><td>数据库兼容的&nbsp;NUMERIC&nbsp;或&nbsp;DECIMAL</td></tr><tr><td>StringTypeHandler</td><td>java.lang.String</td><td>CHAR,&nbsp;VARCHAR</td></tr><tr><td>ClobReaderTypeHandler</td><td>java.io.Reader</td><td>&#8211;</td></tr><tr><td>ClobTypeHandler</td><td>java.lang.String</td><td>CLOB,&nbsp;LONGVARCHAR</td></tr><tr><td>NStringTypeHandler</td><td>java.lang.String</td><td>NVARCHAR,&nbsp;NCHAR</td></tr><tr><td>NClobTypeHandler</td><td>java.lang.String</td><td>NCLOB</td></tr><tr><td>BlobInputStreamTypeHandler</td><td>java.io.InputStream</td><td>&#8211;</td></tr><tr><td>ByteArrayTypeHandler</td><td>byte[]</td><td>数据库兼容的字节流类型</td></tr><tr><td>BlobTypeHandler</td><td>byte[]</td><td>BLOB,&nbsp;LONGVARBINARY</td></tr><tr><td>DateTypeHandler</td><td>java.util.Date</td><td>TIMESTAMP</td></tr><tr><td>DateOnlyTypeHandler</td><td>java.util.Date</td><td>DATE</td></tr><tr><td>TimeOnlyTypeHandler</td><td>java.util.Date</td><td>TIME</td></tr><tr><td>SqlTimestampTypeHandler</td><td>java.sql.Timestamp</td><td>TIMESTAMP</td></tr><tr><td>SqlDateTypeHandler</td><td>java.sql.Date</td><td>DATE</td></tr><tr><td>SqlTimeTypeHandler</td><td>java.sql.Time</td><td>TIME</td></tr><tr><td>ObjectTypeHandler</td><td>Any</td><td>OTHER&nbsp;或未指定类型</td></tr><tr><td>EnumTypeHandler</td><td>Enumeration Type</td><td>VARCHAR-任何兼容的字符串类型，存储枚举的名称(而不是索引)</td></tr><tr><td>EnumOrdinalTypeHandler</td><td>Enumeration Type</td><td>任何兼容的&nbsp;NUMERIC&nbsp;或&nbsp;DOUBLE&nbsp;类型，存储枚举的索引(而不是名称)</td></tr></tbody></table></figure>



<p class="is-style-iw-2em">我们还可以自定义一些MyBatis类型处理器（虽然设计数据库时就应该好好考虑让类型匹配）：</p>



<p class="is-style-iw-2em">例如我们要将java的Boolean对应到数据库中的Integer，其中true=1，false=0，步骤如下：</p>



<h4 class="wp-block-heading">创建转换器</h4>



<p class="is-style-iw-2em">需要实现TypeHandler接口，但是通过阅读源码发现，此接口有一个实现类BaseTypeHandler，因此要实现转换器有2种选择:</p>



<p class="is-style-iw-2em"><strong>1.实现接口TypeHandler接口</strong></p>



<p class="is-style-iw-2em"><strong>2.继承BaseTypeHandler</strong></p>



<p class="is-style-iw-2em">继承接口的实现类要做的工作量肯定比直接实现接口要简单些，所以需要新建一个转换器类继承BaseTypeHandler：</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-68.png" alt="" class="wp-image-3056" /></figure>



<pre class="wp-block-preformatted">public class BooleanAndIntConverter extends BaseTypeHandler&lt;Boolean&gt; {<em>
    </em>@Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Boolean aBoolean, JdbcType jdbcType) throws SQLException {

        if (aBoolean)
        {
            preparedStatement.setInt(i,1);
        }
        else
        {
            preparedStatement.setInt(i,0);
        }
    }

    @Override
    public Boolean getNullableResult(ResultSet resultSet, String s) throws SQLException {
        int sexNum=resultSet.getInt(s);
        return sexNum==1?true:false;
    }

    @Override
    public Boolean getNullableResult(ResultSet resultSet, int i) throws SQLException {
        int sexNum=resultSet.getInt(i);
        return sexNum==1?true:false;
    }

    @Override
    public Boolean getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        int sexNum=callableStatement.getInt(i);
        return sexNum==1?true:false;
    }


}</pre>



<p class="is-style-iw-2em">首先继承的BaseTypeHandler类有一个泛型，这个泛型是java的类型。继承写明了泛型之后，需要实现的方法的java那一段的参数就会自动根据泛型进行格式转化。例如这四个方法中的Boolean。</p>



<p class="is-style-iw-2em">如果不写明泛型，那么这些值的类型就是object：</p>



<pre class="wp-block-preformatted">@Override<br>public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {<br>    <br>}<br><br>@Override<br>public Object getNullableResult(ResultSet resultSet, String s) throws SQLException {<br>    return null;<br>}<br><br>@Override<br>public Object getNullableResult(ResultSet resultSet, int i) throws SQLException {<br>    return null;<br>}<br><br>@Override<br>public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException {<br>    return null;<br>}</pre>



<p class="is-style-iw-2em">四个方法由set由get，其中数据的方向是：</p>



<p class="has-text-align-center">get: DB-&gt; java<br>set:java-&gt; DB</p>



<p class="is-style-iw-2em">在唯一的一个set方法中，参数PreparedStatement preparedStatement可以理解为数据库中的一个元组；int i是PreparedStatement的操作参数位置（一个元组某个属性的列号）；Object o是java那一端的值的对象；JdbcType jdbcType是JDBC中对应的类型，其应该是由preparedStatement的set方法来决定的，例如：</p>



<pre class="wp-block-preformatted">@Override<br>public void setNonNullParameter(PreparedStatement preparedStatement, int i, Boolean aBoolean, JdbcType jdbcType) throws SQLException {<br><br>    if (aBoolean)<br>    {<br>        preparedStatement.setInt(i,1);<br>    }<br>    else<br>    {<br>        preparedStatement.setInt(i,0);<br>    }<br>}</pre>



<p class="is-style-iw-2em">三个get方法是三种从数据库取出值的方式，取出值之后再经过自定义的类型的转化作为返回值传给java：</p>



<pre class="wp-block-preformatted">@Override<br>public Boolean getNullableResult(ResultSet resultSet, String s) throws SQLException {<br>    int sexNum=resultSet.getInt(s);<br>    return sexNum==1?true:false;<br>}<br><br>@Override<br>public Boolean getNullableResult(ResultSet resultSet, int i) throws SQLException {<br>    int sexNum=resultSet.getInt(i);<br>    return sexNum==1?true:false;<br>}<br><br>@Override<br>public Boolean getNullableResult(CallableStatement callableStatement, int i) throws SQLException {<br>    int sexNum=callableStatement.getInt(i);<br>    return sexNum==1?true:false;<br>}</pre>



<h4 class="wp-block-heading">在conf.xml文件中配置</h4>



<p class="is-style-iw-2em">在typeHandlers标签下配置：</p>



<pre class="wp-block-preformatted">&lt;typeHandlers&gt;<em>
        </em>&lt;typeHandler handler="shopkeeper.converter.BooleanAndIntConverter" javaType="boolean" jdbcType="INTEGER"/&gt;
    &lt;/typeHandlers&gt;</pre>



<p class="is-style-iw-2em">typehandler的handler属性要写全类名，之后还要加上javaType和jdbcTypc属性，注意不知道为什么觉得不错Type要全大写。</p>



<h4 class="wp-block-heading">测试</h4>



<p class="is-style-iw-2em">在Mapper.xml中增加使用了类型转化器的语句：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonByIdWithConverter" resultMap="resultPerson" parameterType="int"&gt;<br>    select <em>* </em>from person where id=#{id}<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">此时返回值不再是resultType，而是resultMap了。如果类中属性和表中的字段类型能够合理识别(String-varchar) ，则可以使用resultType，否则使用resultMap；如果类中属性名和表中的字段名能够合理识别，则可以使用resultType，否则使用resultMap。</p>



<p class="is-style-iw-2em">resultMap是需要自己定义的，目的是将java和数据库的值用类型转换器一一映射过去：</p>



<pre class="wp-block-preformatted">&lt;resultMap id="resultPerson" type="person"&gt;<em>
    </em>&lt;id property="id" column="id"/&gt;
    &lt;result property="name"  column="name"/&gt;
    &lt;result property="age" column="age"/&gt;
    &lt;result property="sex" column="sex" javaType="boolean" jdbcType="INTEGER"/&gt;
&lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">resultMap的id属性值就是标识它的东西，type是返回值在java当中的类型，这里已经用了别名了所以只写了person。resultMap下的子标签根据主键和非主键由分为id和result，其中property是java当中的成员变量名，column是数据库的列名。如果某一个元素需要使用自定义的类型转换器，还需要再这个标签内加上javaType和jdbcType。</p>



<p class="is-style-iw-2em">测试类中用到是接口代理的方式，没什么区别。</p>



<p class="is-style-iw-2em">上面这个例子是返回值需要使用类型转换器，下面这个例子是当输入的参数需要使用类型转换器的情况：</p>



<pre class="wp-block-preformatted">&lt;insert id="addPersonWithConverter" parameterType="person"&gt;<br>    insert into person (age,name,id,sex) values (#{age},#{name},#{id},#{sex,javaType=boolean,jdbcType=INTEGER})<br>&lt;/insert&gt;</pre>



<p class="is-style-iw-2em">可以看见需要转换类型的sex，后面加上了javaType和jdbcType。</p>



<h3 class="wp-block-heading">resultMap的功能</h3>



<p class="is-style-iw-2em">在使用类型转换器的时候已经发现了使用resultMap可以实现类型转换，但是还提到当“如果类中属性名和表中的字段名能够合理识别，则可以使用resultType，否则使用resultMap”时也需要使用resultMap。</p>



<p class="is-style-iw-2em">在定义java当中的实体类时，所有成员变量都必须和数据库的列名能够一一对应（数据库不区分大小写），否则将会无法匹配，例如这个语句：</p>



<pre class="wp-block-preformatted">&lt;select id="queryPersonById" resultType="person" parameterType="int"&gt;<br>    select <em>* </em>from person where id=#{id}<br>&lt;/select&gt;</pre>



<p class="is-style-iw-2em">现在Java中有一个成员变量是id，数据库列名中也有id，那么它们是可以匹配的。但是如果将Java中的id改为num，那么就无法匹配了（返回默认值）。</p>



<p class="is-style-iw-2em">在resultMap中需要修改property：</p>



<pre class="wp-block-preformatted">&lt;resultMap id="resultPerson" type="person"&gt;<em>
    </em>&lt;id property="num" column="id"/&gt;
    &lt;result property="name"  column="name"/&gt;
    &lt;result property="age" column="age"/&gt;
    &lt;result property="sex" column="sex" javaType="boolean" jdbcType="INTEGER"/&gt;
&lt;/resultMap&gt;</pre>



<p class="is-style-iw-2em">但是这个情况简直多次一举，因为java的实体类明显是按照数据库来设计的，只有没事找事想增加项目工程量的人才会乱写实体类的成员变量。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3053/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3053</post-id>	</item>
		<item>
		<title>MyBatis学习总结（二）</title>
		<link>https://www.cztcode.com/2020/3033/</link>
					<comments>https://www.cztcode.com/2020/3033/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Wed, 08 Jul 2020 15:24:31 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3033</guid>

					<description><![CDATA[MyBatis学习总结（二） Mybatis进行CRUD]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">Mybatis进行CRUD</h2>



<p class="is-style-iw-2em">之前已经配置了MyBatis的使用环境以及其它一切所需要的基础文件，但是一直没说这个东西究竟时怎么样来操作数据库的。上一次的总结最后的Test类就是干这件事情的。</p>



<h3 class="wp-block-heading">基础方式</h3>



<p class="is-style-iw-2em">在测试类的方法中，显然是要读取conf.xml文件的，然后用这个IO流创建一个SqlSessionFactory（SqlSessionFactoryBuilder().build(reader)，build的第二个参数可以指定数据库环境，默认使用environments的default指定的）；用SqlSessionFactory的openSession方法还能构造一个SqlSession实例。</p>



<pre class="wp-block-preformatted">Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em>SqlSession sqlSession=sqlSessionFactory.openSession();</pre>



<p class="is-style-iw-2em">讲了这么多，其实这个SqlSession实例就相当于JDBC的Connection，不过干的事情更多些：SqlSession不止是声明了数据库信息，它的创建所依据的那个conf.xml文件中还声明了映射文件，所以所有即将用到的sql语句都是SqlSession实例所直接能够使用的；而JDBC中所有的sql语句都要自己现场手写，不易管理、修改起来很不方便。</p>



<p class="is-style-iw-2em">在Mapper.xml中，每一个sql语句都是由namespace和id来唯一标识。而这里增删改查的方式就是直接定位到每一个Mapper.xml中的语句来实现数据库操作，定位方法是直接将这两个东西弄成一个String：</p>



<pre class="wp-block-preformatted">String statement="shopkeeper.personMapper."+"queryAll";</pre>



<p class="is-style-iw-2em">增删改查是调用SqlSession实例的对应的方法，其中第一个参数必须是唯一标识statement，后继的参数如同Mapper.xml文件中的一样如果没有就不写，要有的话只能有一个（多个就封装成对象）；返回值稍作改变，Mapper.xml中无论返回值有多少个，都只写一个的情况，但是Java语言不能这样，要视情况改变（其实全写List也行，反正原理都是从游标读，然后返回值的多少其实是取决于你调用的SqlSession的方法，例如selectOne和selectLst）：</p>



<pre class="wp-block-preformatted">Person person=sqlSession.selectOne(statement,1);
List&lt;Person&gt; persons=sqlSession.selectList(statement);
int count=sqlSession.insert(statement,person);
int count=sqlSession.delete(statement,3);
int count=sqlSession.update(statement,person);</pre>



<p class="is-style-iw-2em">由于我使用的事务方式是JDBC，增删改操作还是要commit的，做法为调用SqlSession.commit()方法。</p>



<p class="is-style-iw-2em">完整的方法如下：</p>



<pre class="wp-block-preformatted">public static void queryPersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br>    String statement="shopkeeper.personMapper.queryPersonById";<br><br>    Person person=sqlSession.selectOne(statement,1);<br><br>    System.<em>out</em>.println(person);<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void queryAllPerson() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br>    String statement="shopkeeper.personMapper."+"queryAll";<br><br>    List&lt;Person&gt; persons=sqlSession.selectList(statement);<br><br>    System.<em>out</em>.println(persons);<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void addPerson() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br>    String statement="shopkeeper.personMapper."+"addPerson";<br><br>    Person person=new Person(19,"刘茜元",3);<br>    int count=sqlSession.insert(statement,person);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("增加了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void deletePersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br>    String statement="shopkeeper.personMapper."+"detelePersonById";<br><br>    int count=sqlSession.delete(statement,3);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("删除了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void updatePersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br>    String statement="shopkeeper.personMapper."+"updatePerson";<br><br>    Person person=new Person();<br>    person.setId(3);<br>    person.setName("刘闹闹");<br>    person.setAge(19);<br>    int count=sqlSession.update(statement,person);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("更新了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}</pre>



<h3 class="wp-block-heading">mapper动态代理方式CRUD</h3>



<p class="is-style-iw-2em">也叫接口开发。</p>



<p class="is-style-iw-2em">在基础方式中说JDBC不好时，点名批评了JDBC每一个sql语句都要手写，但是其实基础方式里面也是要手写每一个statement的，只不过是将sql语句简化为了namespace+id了。</p>



<p class="is-style-iw-2em">但是我们要严于律己，仅仅做到这方面是远远不够的，最好能够达到直接调用方法就能指哪打哪。mapper动态代理就是干的这件事。</p>



<p class="is-style-iw-2em">总的来说，Mapper动态代理的思路就是将Mapper.xml中的sql语句变成接口中的方法，约定之前的namespace成为接口名称，每一个id变成方法名，具体是怎么实现的就是MyBatis底层的事情了。</p>



<p class="is-style-iw-2em">那么，这些接口就需要满足以下几个要求：</p>



<p class="is-style-iw-2em"><em>接口名为namespace；</em></p>



<p class="is-style-iw-2em"><em>方法名为id值；</em></p>



<p class="is-style-iw-2em"><em>输入的参数和mapper.xml的parameterType一致；</em></p>



<p class="is-style-iw-2em"><em>返回值如果是查询就必须和resultType一致，如果你想要得到增删改操作成功的结果数量，就可以写成int，如果不想要就写void。</em></p>



<p class="is-style-iw-2em">有多个Mapper.xml文件就实现多个接口。匹配的过程是根据接口名找到mapper. xml文件，根据接口的方法名找到mapper. xml文件中的SQL标签。（那么问题来了，如果两个不同的mapper.xml中有id相同的sql语句怎么办？对应到java就是两个不同的接口有同名的方法）。</p>



<p class="is-style-iw-2em">还有一个习惯是： SQL映射文件(mapper. xml)和接口放在同一个包中(注意修改conf. xml中加载mapper. xml文件的路径）。</p>



<h4 class="wp-block-heading"><strong>执行</strong></h4>



<p class="is-style-iw-2em">先得到接口的实例化对象：</p>



<pre class="wp-block-preformatted">personMapper personMapper =sqlSession.getMapper(personMapper.class);</pre>



<p class="is-style-iw-2em">再通过接口的实例化对象来调用其方法（返回值自取）：</p>



<pre class="wp-block-preformatted">int count=personMapper.addPerson(person);</pre>



<p class="is-style-iw-2em">最后增删改别忘了commit。</p>



<p class="is-style-iw-2em">完整的方法如下：</p>



<pre class="wp-block-preformatted">public static void queryPersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    Person person=personMapper.queryPersonById(1);<br><br><br>    System.<em>out</em>.println(person);<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void queryPersonByIdWithConverter() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    Person person=personMapper.queryPersonByIdWithConverter(1);<br><br><br>    System.<em>out</em>.println(person);<br>    sqlSession.close();<br>    reader.close();<br>}<br><br><br>public static void queryAllPerson() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    List&lt;Person&gt; persons=personMapper.queryAll();<br><br>    System.<em>out</em>.println(persons);<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void addPerson() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    Person person=new Person(19,"刘茜元",13);<br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    int count=personMapper.addPerson(person);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("增加了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void addPersonWithConverter() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    Person person=new Person(19,"刘茜元",13);<br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    int count=personMapper.addPersonWithConverter(person);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("增加了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void deletePersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    int count=personMapper.detelePersonById(13);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("删除了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}<br><br>public static void updatePersonById() throws IOException {<br>    Reader reader=Resources.<em>getResourceAsReader</em>("conf.xml");<br>    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);<em>//</em><em>可以通过</em><em>build</em><em>的第二参数指定数据库环境<br></em><em>    </em>SqlSession sqlSession=sqlSessionFactory.openSession();<br><br>    personMapper personMapper =sqlSession.getMapper(personMapper.class);<br>    Person person=new Person();<br>    person.setId(13);<br>    person.setName("刘咯咯");<br>    person.setAge(19);<br>    int count=personMapper.updatePerson(person);<br>    sqlSession.commit();<br><br>    System.<em>out</em>.println("更新了"+count+"个人");<br>    sqlSession.close();<br>    reader.close();<br>}</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3033/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3033</post-id>	</item>
		<item>
		<title>Mybatis学习日志（一）</title>
		<link>https://www.cztcode.com/2020/3032/</link>
					<comments>https://www.cztcode.com/2020/3032/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Wed, 08 Jul 2020 14:31:54 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3032</guid>

					<description><![CDATA[MyBatis 可以简化 JDBC 操作，实现数据的持久化。 MyBatis 是一种ORM 产品, ORM的实现。ORM： Objective Relational Mapping （ORM即可以将一个person对象和一个person表对应起来）。 MyBatis 下载与安装 先进入MyBatis官网 https://mybatis.org/mybatis-3/zh/index.html 进入官 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>MyBatis  可以简化  JDBC  操作，实现数据的持久化。</p>



<p>MyBatis 是一种ORM 产品, ORM的实现。ORM： Objective Relational Mapping         （ORM即可以将一个person对象和一个person表对应起来）。</p>



<h3 class="wp-block-heading">MyBatis 下载与安装</h3>



<p>先进入MyBatis官网     <a href="https://mybatis.org/mybatis-3/zh/index.html" target="_blank" rel="noopener">https://mybatis.org/mybatis-3/zh/index.html</a></p>



<p>进入官网  点击 <strong>“  简体中文  ”</strong></p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-57-1024x517.png" alt="" class="wp-image-3034" /></figure>



<p>进入这个界面  ,点击     <a href="https://github.com/mybatis/mybatis-3/releases" target="_blank" rel="noopener">mybatis-x.x.x.jar</a>   </p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-58-1024x422.png" alt="" class="wp-image-3035" /></figure>



<p>点击下载   mybatis-3.5.5.zip 即可。</p>



<figure class="wp-block-image size-large is-resized"><img fetchpriority="high" decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-59.png" alt="" class="wp-image-3036" width="580" height="300" /></figure>



<p>下载之后，解压，解压之后，会得到以下文件</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-60.png" alt="" class="wp-image-3037" /></figure>



<p>PDF文件介绍了MyBatis怎么使用，是文档说明书。可直接从中赋值所用的代码。                        </p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3032/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3032</post-id>	</item>
		<item>
		<title>MyBatis学习总结（一）</title>
		<link>https://www.cztcode.com/2020/3008/</link>
					<comments>https://www.cztcode.com/2020/3008/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Tue, 07 Jul 2020 14:31:58 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3008</guid>

					<description><![CDATA[MyBatis学习总结（一） 有关MyBatis起步]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading">有关MyBatis起步</h2>



<p class="is-style-iw-2em">一个可以基本实现运行的工程的目录结构如下：</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-53.png" alt="" class="wp-image-3009" /></figure>



<p class="is-style-iw-2em">需要依赖数据库jar包和mybatisjar包：</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-54.png" alt="" class="wp-image-3010" /></figure>



<h3 class="wp-block-heading">conf.xml文件</h3>



<p class="is-style-iw-2em">conf.xml可以简化JDBC操作，实现数据的持久化。其配置数据库信息和需要加载的映射文件。这个文件完成的工作是访问数据库、创建数据库操作语句等等。本来这些东西在JDBC当中要写很多遍，但是每次写的无非就那几句，所以这个conf不仅是必要的，也是充分的。</p>



<pre class="wp-block-preformatted">&lt;configuration&gt;<em>
    </em>&lt;environments default="development"&gt;<em>
        </em>&lt;environment id="development"&gt;<em>
            </em>&lt;transactionManager type="JDBC"/&gt;<em>
            </em>&lt;dataSource type="POOLED"&gt;
                &lt;property name="driver" value="com.mysql.cj.jdbc.Driver"/&gt;
                &lt;property name="url" value="jdbc:mysql://localhost:3306/person?useUnicode=true&amp;amp;characterEncoding=utf8&amp;amp;serverTimezone=GMT"/&gt;
                &lt;property name="username" value="root"/&gt;
                &lt;property name="password" value="password"/&gt;
            &lt;/dataSource&gt;
        &lt;/environment&gt;
    &lt;/environments&gt;
    &lt;mappers&gt;
        &lt;mapper resource="shopkeeper\mapper\personMapper.xml"/&gt;
    &lt;/mappers&gt;
&lt;/configuration&gt;</pre>



<p class="is-style-iw-2em">conf文件规定了：</p>



<p class="is-style-iw-2em"><strong>一、数据库信息</strong></p>



<p class="is-style-iw-2em">MyBatis运行时的数据库环境。通过environments标签即其下属的environment标签可以声明可用的数据库环境，通过environments的default属性来指定运行时默认的数据库环境。</p>



<p class="is-style-iw-2em">事务的提交方式和数据源类型。每一个environment都有一个transactionManager标签，其Type属性规定了事务提交方式。可取值有，JDBC :利用JDBC方式处理事务(commit rollback close)；MANAGED:将事务交由其他组件去托管(spring， jboss）。environment的另一个标签是dataSource，其Type属性规定了数据源类型。可取值为，UNPOOLED:传统的JDBC模式(每次访问数据库，均需要打开、关闭等数据库操作，但是打开、关闭数据库是比较消耗性能的）；POOLED:使用数据库连接池；JNDI:从tomcat中获取一个内置的数据库连接池(数据库连接池-数据源）,默认会关闭连接。</p>



<p class="is-style-iw-2em">dataSource下的标签声明了打开某个数据库的操作，从驱动到数据库位置到账户密码都和JDBC如出一辙。</p>



<p class="is-style-iw-2em">conf.xml</p>



<pre class="wp-block-preformatted">&lt;dataSource type="POOLED"&gt;<br>    &lt;property name="driver" value="com.mysql.cj.jdbc.Driver"/&gt;<br>    &lt;property name="url" value="jdbc:mysql://localhost:3306/person?useUnicode=true&amp;amp;characterEncoding=utf8&amp;amp;serverTimezone=GMT"/&gt;<br>    &lt;property name="username" value="root"/&gt;<br>    &lt;property name="password" value="password"/&gt;<br>&lt;/dataSource&gt;</pre>



<p class="is-style-iw-2em">JDBC</p>



<p>try<br>{<br>Class.forName(&#8220;com.mysql.cj.jdbc.Driver&#8221;);<br>Connection conn=DriverManager.getConnection(&#8220;jdbc:mysql://localhost:3306/STUDB?serverTimezone=UTC&#8221;, &#8220;root&#8221;, &#8220;password&#8221;);<br>&#8230;<br>conn.close();<br>}<br>catch(Exception e)<br>{<br>System.err.println(&#8220;异常&#8221;+e);<br>}</p>



<p class="is-style-iw-2em"><strong>二、映射文件</strong></p>



<p class="is-style-iw-2em">conf.xml还规定了绑定的映射文件Mapper.xml，这样我们在读取conf.xml时还能连带着读取到Mapper.xml的路径，这样就可以访问它了。所以这个Mapper时什么东西，我将在下下个标题总结。</p>



<h3 class="wp-block-heading">实体类</h3>



<p class="is-style-iw-2em">实体类将数据库的一张表映射成为一个对象，这样我们可以如同操作一个实体对象一般操作数据库的一张表。</p>



<pre class="wp-block-preformatted">public class Person {<br><br>    private int age;<br>    private String name;<br>    private int id;<br><br>    public Person() {<br>    }<br><br>    public Person(int age, String name, int id) {<br>        this.age = age;<br>        this.name = name;<br>        this.id = id;<br>    }<br><br>    public int getAge() {<br>        return age;<br>    }<br><br>    public void setAge(int age) {<br>        this.age = age;<br>    }<br><br>    public String getName() {<br>        return name;<br>    }<br><br>    public void setName(String name) {<br>        this.name = name;<br>    }<br><br>    public int getId() {<br>        return id;<br>    }<br><br>    public void setId(int id) {<br>        this.id = id;<br>    }<br><br>    @Override<br>    public String toString() {<br>        return "Person{" +<br>                "age=" + age +<br>                ", name='" + name + '\'' +<br>                ", id=" + id +<br>                '}';<br>    }<br>}</pre>



<h3 class="wp-block-heading">Mapper.xml</h3>



<p class="is-style-iw-2em">直截了当的来讲，Mapper就是写了MyBatis下操作数据库时的数据操作语句。</p>



<pre class="wp-block-preformatted">&lt;mapper namespace="shopkeeper.mapper.personMapper"&gt;<em>
    &lt;!--parameterType :输入参数的类型
    resultType:查询返回结果值的类型，返回类型--&gt;
    </em>&lt;select id="queryPersonById" resultType="shopkeeper.entity.Person" parameterType="int"&gt;
        select <em>* </em>from person where id=#{id}
    &lt;/select&gt;
    
    &lt;insert id="addPerson" parameterType="shopkeeper.entity.Person"&gt;
        insert into person (age,name,id) values (#{age},#{name},#{id})
    &lt;/insert&gt;
    
    &lt;update id="updatePerson" parameterType="shopkeeper.entity.Person"&gt;
        update person set age=#{age},name =#{name} where id=#{id}
    &lt;/update&gt;

    &lt;delete id="detelePersonById" parameterType="int"&gt;
        delete from person where id=#{id}
    &lt;/delete&gt;

    &lt;select id="queryAll" resultType="shopkeeper.entity.Person"&gt;
        select <em>* </em>from person
    &lt;/select&gt;
&lt;/mapper&gt;</pre>



<p class="is-style-iw-2em">因为conf.xml中可能会映射多个Mapper文件，所以需要能唯一标识每一个Mapper。mapper标签的namespace就是干的这件事情。</p>



<p class="is-style-iw-2em">在mapper标签里面就可以写这些sql语句了，同样的，也需要唯一标识一个mapper的一条sql语句，其属性id就是干的这件事。</p>



<p class="is-style-iw-2em">后续我们就会使用namespace+id来确定究竟使用哪条语句。</p>



<p class="is-style-iw-2em">此外，每一条sql，parameterType是输入参数的类型，resultType是查询返回结果值的类型并且有以下约定：</p>



<p class="is-style-iw-2em">输入参数parameterType 和输出参数resultType 在形式上都只能有一个；</p>



<p class="is-style-iw-2em">如果输入/输出参数:是简单类型(8个基本类型+String) 的话，可以使用任何占位符# {xxxx}；如果是对象类型，则必须是对象的属性#属性名；</p>



<p class="is-style-iw-2em">输出参数: 如果返回值类型是一个对象(如Person) ,则无论返回一个还是多个，在resultType都写成一个。</p>



<h3 class="wp-block-heading">Test类</h3>



<p class="is-style-iw-2em">在test类中将会真正使用Mybatis来访问数据库并进行操作，有两种操作方式，将在下一次总结中进行记录。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3008/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3008</post-id>	</item>
		<item>
		<title>Mybatis缓存</title>
		<link>https://www.cztcode.com/2020/2964/</link>
					<comments>https://www.cztcode.com/2020/2964/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Mon, 06 Jul 2020 02:22:08 +0000</pubDate>
				<category><![CDATA[MyBatis]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=2964</guid>

					<description><![CDATA[Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中，执行相同的查询SQL，第一次会去数据库进行查询，并写到缓存中； 第二次以后是直接去缓存中取。 当执行SQL查询中间发生了增删改的操作，MyBatis会把SqlSession的缓存清空。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<h2 class="wp-block-heading">一级缓存</h2>



<p class="is-style-iw-2em">Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中，执行相同的查询SQL，第一次会去数据库进行查询，并写到缓存中； 第二次以后是直接去缓存中取。 当执行SQL查询中间发生了增删改的操作，MyBatis会把SqlSession的缓存清空。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-38-1024x298.png" alt="" class="wp-image-2965"/></figure>



<h2 class="wp-block-heading">二级缓存</h2>



<p class="is-style-iw-2em">二级缓存<br>Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容，多个SqlSession共享。Mybatis需要手动设置启动二级缓存。</p>



<p class="is-style-iw-2em">二级缓存是默认启用的(要生效需要对每个Mapper进行配置)，如想取消，则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。</p>



<pre class="wp-block-code"><code>&lt;settings>
  &lt;setting name="cacheEnabled" value="false" />
&lt;/settings></code></pre>



<p class="is-style-iw-2em">由于是根据namespace来调整的，所以在使用二级缓存时需要修改mapper.xml，开启缓存</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-39.png" alt="" class="wp-image-2966"/></figure>



<p class="is-style-iw-2em">在二级缓存中，mybatis是将类的信息序列化保存在硬盘上，所以映射的类（包括属性类）需要继承序列化。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-40.png" alt="" class="wp-image-2967"/></figure>



<p class="is-style-iw-2em">效果：</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-41.png" alt="" class="wp-image-2968"/></figure>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-42-1024x150.png" alt="" class="wp-image-2969"/></figure>



<h4 class="wp-block-heading">Cache Hit Ratio 表示缓存命中率。</h4>



<p class="is-style-iw-2em">开启二级缓存后，每执行一次查询，系统都会计算一次二级缓存的命中率。<br>第一次查询也是先从缓存中查询，只不过缓存中一定是没有的。<br>所以会再从DB中查询。由于二级缓存中不存在该数据，所以命中率为0.但第二次查询是从二级缓存中读取的，所以这一次的命中率为1/2=0.5。<br>当然，若有第三次查询，则命中率为1/3=0.66 。</p>



<h3 class="wp-block-heading">注意:</h3>



<p class="is-style-iw-2em">增删改操作，无论是否进行提交sqlSession.commit()，均会清空一级、二级缓存，使查询再次从DB中select。</p>



<h4 class="wp-block-heading">二级缓存的使用原则</h4>



<p class="is-style-iw-2em">只能在一个命名空间下使用二级缓存，由于二级缓存中的数据是基于namespace的，即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作，那么这多个namespace中的数据可能就会出现不一致现象。</p>



<h5 class="wp-block-heading">在单表上使用二级缓存</h5>



<p class="is-style-iw-2em">如果一个表与其它表有关联关系，那么非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰，所以就有可能出现多个namespace中的数据不一致现象。</p>



<h4 class="wp-block-heading">查询多于修改时使用二级缓存</h4>



<p class="is-style-iw-2em">在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存，对二级缓存的频繁刷新将降低系统性能。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/2964/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2964</post-id>	</item>
	</channel>
</rss>
