<?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>Java &#8211; Blog of Code</title>
	<atom:link href="https://www.cztcode.com/category/java/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.cztcode.com</link>
	<description></description>
	<lastBuildDate>Tue, 08 Dec 2020 04:19:49 +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>Java &#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>IDEA JSP 热部署</title>
		<link>https://www.cztcode.com/2020/3714/</link>
					<comments>https://www.cztcode.com/2020/3714/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Tue, 08 Dec 2020 04:19:49 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3714</guid>

					<description><![CDATA[Web 项目开发时，经常会修改 resource 文件和 Java 文件，往往需要重新部署才能生效。
热部署，即不需要重新部署，就能生效。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>Web 项目开发时，经常会修改 resource 文件和 Java 文件，往往需要重新部署才能生效。<br>热部署，即不需要重新部署，就能生效。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>以下基于 IDEA 实现热部署</p></blockquote>



<h2 class="wp-block-heading" id="部署方式">部署方式</h2>



<p>两种部署方式：war 和 war exploded</p>



<pre class="wp-block-code"><code>war：将WEB工程以包的形式上传到服务器 ；
war exploded模式：将WEB工程以当前文件夹的位置关系上传到服务器；
</code></pre>



<p>（1）war 模式这种可以称之为是发布模式，看名字也知道，这是先打成 war 包，再发布；</p>



<p>（2）war exploded 模式是直接把文件夹、JSP 页面 、classes 等等移到 Tomcat 部署文件夹里面，进行加载部署。因此这种方式支持热部署，一般在开发的时候也是用这种方式。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>获取上下文路径时，第一种方式基于 Tomcat 位置；第二种基于项目 target 位置</p></blockquote>



<h2 class="wp-block-heading" id="on-update-action与on-frame-deactivation">on update action 与 on frame deactivation</h2>



<h3 class="wp-block-heading" id="on-update-action">on update action</h3>



<p>顾名思义触发 update 时，执行以下哪种动作<br>update resources &#8212;- 更新静态的资源，比如 html,js,css 等 运行模式和调试模式都是立即生效。<br>update classes and resources &#8212;- 更新 java,jsp 和静态资源（<br>1. Java 修改后，会被编译成。class 然后覆盖到 target/kao 文件夹下，IDE 调试模式的情况下，立即生效。IDE 运行模式下，不立即生效，需要 redeployed 才可生效。<br>2. JSP 修改后，再次被访问的时候，会自动更新，重新编译成.java&#8212;-&gt;.class 保存在 Tomcat 的 work 目录下。由于是访问时才检测是否修改，是否需要重新编译，所以 IDE 运行模式 和 IDE 调试模式下，都是立即生效。刷新下页面就可）；<br>redeployed &#8212;&#8211; 重新部署，发布到 Tomcat 里，不重启 Tomcat，而是把原来的删掉，然后重新发布；<br>restart server &#8212;&#8211; 重启 Tomcat</p>



<h3 class="wp-block-heading" id="on-frame-deactivation">on frame deactivation</h3>



<p>顾名思义 IDEA 窗口失去焦点时，触发以下哪种动作<br>Do nothing &#8212;&#8212;&#8212; 什么都不做<br>update resources &#8212;- 更新静态的资源，比如 html,js,css 等 运行模式和调试模式都是立即生效。<br>update classes and resources &#8212;- 更新 java,jsp 和静态资源 同上</p>



<h4 class="wp-block-heading">HTML CSS 修改后不更新</h4>



<p>完成上面的操作，很大一部分发现html的修改能够及时更新了，但是js或者jsp的修改却一直没有更新，这是为什么呢？<br>答案是：<strong>浏览器的坑</strong></p>



<p>我是使用的chrome，F12到调试窗口</p>



<p>勾选禁用缓存</p>



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



<h2 class="wp-block-heading" id="总结">总结</h2>



<p>on update action 设置为 update classes and resources<br>on frame deactivation 设置为 do nothing</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>on frame deactivation 默认也是 do nothing，不然你每次切个窗口，都会更新<br>值得注意的是，类似于 application 这种配置文件应该是不会更新，需要重新部署才能生效</p></blockquote>



<p>作者：kevin2020<br>链接：https://ld246.com/article/1516041883689<br>来源：链滴<br>协议：CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3714/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3714</post-id>	</item>
		<item>
		<title>java web 笔记3 JDBC基础</title>
		<link>https://www.cztcode.com/2020/3652/</link>
					<comments>https://www.cztcode.com/2020/3652/#comments</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Tue, 27 Oct 2020 13:12:48 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3652</guid>

					<description><![CDATA[1、JDBC：java DataBase Connectivity 可以为多种关系型DBMS 2、JDBC API 主要功能： 三件事，具体是通过以下类/实现：DriverManager:管理jdbc驱动 Connection:连接（通过DriverManager产生） Statement(PreparedStatement) :增删改查（通过Connection产生）CallableStatem [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="has-medium-font-size"><strong>1、JDBC：java DataBase Connectivity 可以为多种关系型DBMS</strong></p>



<p class="has-medium-font-size"><strong><br>2、JDBC API 主要功能：</strong></p>



<figure class="wp-block-image size-large is-resized"><img fetchpriority="high" decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/10/jdbc-API.png" alt="" class="wp-image-3653" width="580" height="293" /></figure>



<p><br>三件事，具体是通过以下类/实现：<br>DriverManager:管理jdbc驱动</p>



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



<p><br>Connection:连接（通过DriverManager产生）</p>



<p>Statement(PreparedStatement) :增删改查（通过Connection产生）<br>CallableStatement:调用数据库中的存储过程/存储函数（通过Connection产生）</p>



<p>Result:返回的结果集（上面的Statement等产生）</p>



<p>Connection产生操作数据库的对象：</p>



<p>Connection产生Statement对象：createStatement()<br>Connection产生PreparedStatement对象：prepareStatement()<br>Connection产生CallableStatement对象：prepareCall();</p>



<p>Statement操作数据库：<br>增删改：executeUpdate()<br>查询：executeQuery();</p>



<p>ResultSet:保存结果集 select * from xxx<br>next():光标下移，判断是否有下一条数据；true/false<br>previous(): true/false<br>getXxx(字段名|位置): 获取具体的字段值</p>



<p>PreparedStatement操作数据库：<br>public interface PreparedStatement extends Statement<br>因此<br>增删改：executeUpdate()<br>查询：executeQuery();<br>赋值操作 setXxx();</p>



<p>PreparedStatement与Statement在使用时的区别：<br>① Statement:<br>sql<br>executeUpate(sql)<br>② PreparedStatement：<br>sql(可能存在占位符？)<br>在创建PreparedStatement 对象时，将sql预编译 prepareStatement(sql)<br>executeUpate()<br>setXxx()替换占位符？</p>



<p>推荐使用PreparedStatement：原因如下：<br>① 编码更加简便（避免了字符串的拼接）<br>String name =&#8221;zs&#8221;;<br>int age = 23;</p>



<p>stmt:<br>String sql = &#8220;insert into student(stuno,stuname) values(&#8216;&#8221;+name+&#8221;, &#8220;+age+&#8221;&#8216;)&#8221;;<br>stmt.executeUpate(sql);</p>



<p>pstmt:<br>String sql=&#8221;insert into student(stuno,stuname) values(?, ?)&#8221;;<br>pstmt = connection.prepareStatement(sql);//预编译SQL<br>pstmt.setString(1,name);<br>pstmt.setInt(2,age);<br>pstmt.executeUpate();</p>



<p>②提高性能（因为有预编译操作，预编译只需要执行一次）<br>需要重复增加100条数<br>stmt:<br>String sql = &#8220;insert into student(stuno,stuname) values(&#8216;&#8221;+name+&#8221;, &#8220;+age+&#8221;&#8216;)&#8221;;<br>for(100)<br>{<br>stmt.executeUpate(sql);<br>}</p>



<p>pstmt:<br>String sql=&#8221;insert into student(stuno,stuname) values(?, ?)&#8221;;<br>pstmt = connection.prepareStatement(sql);//预编译SQL<br>pstmt.setString(1,name);<br>pstmt.setInt(2,age);<br>for(100)<br>{<br>pstmt.executeUpate();<br>}</p>



<p>③安全（可以有效防止sql注入）<br>sql注入：将客户输入的内容和开发人员的sql语句混为一体</p>



<p>stmt:存在被sql注入的风险<br>（例如输入 用户名：任意值’ or 1=1 &#8212;<br>密码：任意值）<br>分析:<br>select count(<em>) from login where uname=&#8217;任意值&#8217; or 1=1 &#8211;&#8216; and upwd=&#8217;任意值&#8217;; select count(</em>) from login where uname=&#8217;任意值&#8217; or 1=1;<br>select count(*) from login;</p>



<p>select count(*) from login where uname='&#8221;+name+&#8221;&#8216; and upwd='&#8221;+pwd+&#8221;&#8216;<br>pstmt:防止sql注入</p>



<p class="has-medium-font-size"><strong>3、jdbc访问数据库的具体步骤：</strong></p>



<p><br>a.导入驱动，加载具体的驱动类<br>b.与数据库建立连接<br>c.发送sql，执行<br>d.处理结果集（查询）</p>



<p class="has-medium-font-size"><strong>4.数据库驱动</strong></p>



<p><br>驱动 jar 具体驱动类 连接字符串<br>Oracle ojdbc-x.jar oracle.jdbc.OracleDriver jdbc：oracle:thin:@localhost:1521:ORCL<br>MySQL mysql-connector-java-x.jar com.mysql.jdbc.Driver jdbc:mysql://localhost:3306//数据库实例名<br>SqlServer sql.jdbc-x.jar com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc：microsoft:sqlserver:localhost:1433；databasename=数据库实例名</p>



<p>使用jdbc操作数据库时，如果对数据库进行了更换，只需要替换：驱动、具体驱动类、连接字符串、用户名、密码</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3652/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3652</post-id>	</item>
		<item>
		<title>JAVA WEB 笔记1</title>
		<link>https://www.cztcode.com/2020/3534/</link>
					<comments>https://www.cztcode.com/2020/3534/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Fri, 18 Sep 2020 12:08:58 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3534</guid>

					<description><![CDATA[1.JSP:动态网页 静态、动态:a.不用 和 是否有“动感”混为一谈b.是否 随着 时间、地点、用户操作 的 改变而改变 动态网页 需要使用到 服务端脚本语言（JSP） 2架构 CS:Clinent ServerCS不足：a.如果 软件升级， 那么全部软件都要升级b.维护麻烦: 需要维护每一台 客户端软件c.每一台客户端 都需要安装 客户端软件 BS : broswer Server客户端可以通 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="has-large-font-size"><strong>1.JSP:动态网页</strong></p>



<p class="has-medium-font-size">静态、动态:<br>a.不用 和 是否有“动感”混为一谈<br>b.是否 随着 时间、地点、用户操作 的 改变而改变</p>



<p class="has-medium-font-size">动态网页 需要使用到 服务端脚本语言（JSP）</p>



<p class="has-large-font-size"><strong>2架构</strong></p>



<p class="has-medium-font-size"><br>CS:Clinent Server<br>CS不足：<br>a.如果 软件升级， 那么全部软件都要升级<br>b.维护麻烦: 需要维护每一台 客户端软件<br>c.每一台客户端 都需要安装 客户端软件</p>



<p class="has-medium-font-size">BS : broswer Server<br>客户端可以通过 浏览器 直接访问服务端</p>



<p class="has-medium-font-size">注意：bs和cs各有优势。</p>



<p class="has-large-font-size"><strong>3.tomcat解压后目录：</strong></p>



<p class="has-medium-font-size"><br>bin:可执行文件（startup.bat shuntdown.bat）<br>conf:配置文件（server.xml）<br>lib: tomcat依赖的jar文件<br>log:日志文件（记录出错等信息）<br>temp：临时文件<br>webapps: 可执行的项目（将我们开发的项目 放入该目录）<br>work：存放由jsp翻译成的java，以及编译成的class文件（jsp -&gt;java -&gt;class）</p>



<p class="has-large-font-size"><strong>4.配置tomcat</strong></p>



<p class="has-medium-font-size"><br>a.配置jdk（必须配置JAVA_HOME）<br>java_home classPath path<br>b.配置catalina_home</p>



<p>双击bin/startup.bat启动tomcat<br>常见错误： 可能与其他服务器的端口号冲突<br>tomcat端口号默认8080 （此端口号较为常见， 容易冲突），建议修改次端口（8888）</p>



<p>修改端口号：conf/server.xml中找到 port=&#8221;8080&#8243; 修改数值即可</p>



<p class="has-large-font-size"><strong>5.访问tomcat（默认的端口号8080）</strong></p>



<p class="has-medium-font-size"><br>http://localhost:8888/</p>



<p>常见状态码：<br>200：一切正常<br>300/301：页面重定向 （跳转）<br>404：资源不存在<br>403：权限不足 （如果访问a目录，但是a目录设置 不可见）<br>500：服务器内部错误（代码错误）<br>其他编码：积累</p>



<p>jsp：在html中嵌套的java代码</p>



<p>在项目/WEB-INF/web.xml中设置 默认的 初始页面<br>index.jsp</p>



<p class="has-large-font-size"><strong>6.虚拟路径</strong></p>



<p class="has-medium-font-size"><br>a.方式一<br>将web项目配置到 webapps以外的目录<br>conf/server.xml中配置<br>host标签中：</p>



<p class="has-medium-font-size">docBase: 实际路径<br>path: 虚拟路径 （绝对路径、相对路径【相对于webapps】）<br>重启</p>



<p class="has-medium-font-size">b.方式二</p>



<p class="has-medium-font-size">D:\tomcat\apache-tomcat-9.0.38\conf\Catalina\localhost<br>中新建 “项目名.xml”中新增一行：<br></p>



<p class="has-large-font-size"><strong>7.虚拟主机</strong></p>



<p class="has-medium-font-size"><br>通过www.text.com访问本机<br>a. conf/server.xml</p>



<pre class="wp-block-code"><code>&lt;Host appBase="D:\study\JspProject"  name="www.text.com"&gt;
        &lt;Context  docBase="D:\study\JspProject"    path="/"  /&gt;
&lt;/Host&gt;</code></pre>



<p class="has-medium-font-size">b.C:\Window\System32\drivers\etc\host<br>增加<br>127.0.0.1        www.test.com</p>



<p class="has-medium-font-size">流程：<br>www.test.com -&gt; host找映射关系 -&gt;server.xml找Engine的defaultHost -&gt;通过“/”映射到D:\study\JspProject</p>



<p class="has-large-font-size"><strong>8.JSP执行流程</strong></p>



<p class="has-medium-font-size"><br>jsp-&gt; java(Servlet文件) -&gt;class<br>D:\tomcat\apache-tomcat-9.0.38\work\Catalina\localhost\JspProject\org\apache\jsp</p>



<p class="has-medium-font-size">Jsp 和Servlet 可以相互转换</p>



<p class="has-medium-font-size">因为第一次请求服务端 会有翻译 和编译的过程，因此比较慢； 后续访问 可以直接访问class,因此速度较快。但是 如果 服务端修改了代码，则再次访问时 会重新的翻译、编译。</p>



<p class="has-large-font-size"><strong>9.使用Eclipse开发Web项目（JSP项目）tomcat 在Eclipse中创建的Web项目：</strong></p>



<p class="has-medium-font-size"><br>浏览器可以直接访问 WebContent中的文件<br>例如HTTP：//localhost:8888/MyJspProject/index1.jsp<br>其中的index1.jsp就在WebContent目录；<br>但是WEB-INF中的文件 无法通过客户端直接访问，只能通过请求转发来访问</p>



<p class="has-medium-font-size">注意：并不是 任何的内部跳转都能访问WEB-INF;原因是 跳转有2种方式：请求转发、重定向</p>



<p class="has-large-font-size"><strong>10.配置tomcat运行时环境</strong></p>



<p class="has-medium-font-size"><br>jsp&lt;-&gt;Servlet<br>a.将tomcat/lib中的servlet-api.jar加入项目的构建路径<br>b.右键项目-&gt;Biuld Path -&gt; Add libraryary -&gt;Server Runtime</p>



<p class="is-style-default has-large-font-size"><strong>11.统一字符集编码</strong></p>



<p class="has-medium-font-size"><br>a.编码分类：<br>设置jsp文件的编码（jsp文件中的pageEncoding属性）： </p>



<p class="has-medium-font-size">jsp -&gt; java</p>



<p class="has-medium-font-size"><br>设置浏览器读取jsp文件的编码(jsp文件中content属性)<br>一般将上述设置成一致的编码，推荐使用UTF-8<br>文本编码：<br>i.将整个eclipse中的文件统一设置（推荐）<br>ii.设置某一个项目<br>iii.设置单独文件</p>



<p></p>



<figure class="wp-block-image is-resized is-style-default"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/09/086-150x150.jpg" alt="" width="150" /></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3534/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3534</post-id>	</item>
		<item>
		<title>Java第九章 用户图形界面</title>
		<link>https://www.cztcode.com/2020/3395/</link>
					<comments>https://www.cztcode.com/2020/3395/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Fri, 14 Aug 2020 07:17:24 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3395</guid>

					<description><![CDATA[Java第九章 用户图形界面]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<h2 class="wp-block-heading">9.1 Applet 概述</h2>



<p class="is-style-iw-2em">一种嵌入HTML文档中的Java程序 。</p>



<p class="is-style-default">与Application相比，Applet具有明显的优点 </p>



<ol class="wp-block-list"><li>Web浏览器提供了运行Applet所需要的许多功能 </li><li>Applet是在运行时通过网络从服务器端下载的，因而便于软件的发布和及时更新 </li></ol>



<p class="is-style-default">Applet也有其局限性</p>



<ol class="wp-block-list"><li> 不能在客户机上读写当地文件 </li><li>不能连接除它所在的服务器以外的其它机器</li></ol>



<h2 class="wp-block-heading">9.2 JApplet类</h2>



<p class="is-style-iw-2em">一个applet程序需要继承 javax.swing包中的JApplet类</p>



<pre class="wp-block-code"><code>import java.awt.Graphics;
import javax.swing.JApplet; 
public class MyApplet extends JApplet{  
    public void paint(Graphics g) { //重载JApplet类的paint方法
              //参数是Graphics类的对象，是由浏览器传递过来的
         super.paint(g); //调用其超类JApplet类的paint方法
         g.drawString("This is a Java Applet!",25,25); 
     }
 } </code></pre>



<p class="is-style-default">JApplet类提供了applet在浏览器中运行需要具备的特定方法</p>



<ul class="wp-block-list"><li> 浏览器载入时，要依次运行init、start、paint方法 </li><li>离开浏览器页面时，执行stop </li><li>退出浏览器时，执行destroy</li></ul>



<figure class="wp-block-table"><table><tbody><tr><td><strong>public void start()</strong></td><td><strong>init</strong><strong>方法执行结束之后</strong><strong>，自动调用这个方法。另外，当浏览器用户在访问另一个网址之后</strong><strong>重新返回</strong><strong>applet</strong><strong>所在的</strong><strong>HTML</strong><strong>页</strong><strong>时，将再次调用</strong><strong>start</strong><strong>方法</strong></td></tr><tr><td><strong>public void paint(Graphics g)</strong></td><td><strong>start</strong><strong>方法启动后</strong><strong>调用此方法，另外每次需要</strong><strong>重绘</strong><strong>applet</strong><strong>时</strong><strong>也将调用该方法。程序通常不直接调用</strong><strong>paint</strong></td></tr><tr><td><strong>public void repaint()</strong></td><td><strong>在响应用户和</strong><strong>Applet</strong><strong>的交互时经常要用到。通常</strong><strong>只是调用，而不重写</strong><strong>这个方法。对于轻量级组件，它</strong><strong>调用组件的</strong><strong>paint</strong><strong>方法，对重量级组件它</strong><strong>调用组件的</strong><strong>update</strong><strong>方法，由</strong><strong>update</strong><strong>调用</strong><strong>paint</strong></td></tr><tr><td><strong>public void stop()</strong></td><td><strong>用户</strong><strong>离开</strong><strong>Applet</strong><strong>所在的</strong><strong>HTML</strong><strong>页时调用</strong><strong>该方法</strong><strong>,</strong><strong>它执行挂起</strong><strong>Applet</strong><strong>所需的所有任务，例如停止动画和线程</strong></td></tr><tr><td><strong>public void destroy()</strong></td><td><strong>用户</strong><strong>关闭浏览器窗口</strong><strong>，</strong><strong>Applet</strong><strong>将从内存中移走的时候调用该方法</strong></td></tr></tbody></table></figure>



<h2 class="wp-block-heading">9.3 绘图</h2>



<p class="is-style-iw-2em">import java.awt.* 导入字体 颜色 图形包</p>



<h2 class="wp-block-heading">9.4 Swing基础</h2>



<p class="is-style-default">图形界面设计的一般步骤</p>



<ol class="wp-block-list"><li>选择容器。</li><li>确定布局。</li><li>向容器中添加组件。</li><li>进行事件处理。</li></ol>



<pre class="wp-block-code"><code>应用Swing组件的Applet
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SwingApplet extends JApplet{
   public void init() {
      Container contentPane=getContentPane();
      contentPane.setLayout(new GridLayout(2,1));
      JButton button=new JButton("Click me");
      final JLabel label=new JLabel();
      contentPane.add(button);
      contentPane.add(label);
      button.addActionListener( new ActionListener(){
         public void actionPerformed(ActionEvent event) {
           String information=JOptionPane.showInputDialog("请输入一串字符");
           label.setText(information);
          }  }  );//创建监听器语句结束
  }//init方法结束  
}</code></pre>



<pre class="wp-block-code"><code>把JApplet换成JFrame，将例9-4的程序改为Application
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SwingApplication {
    public static void main(String&#91;] args){
        JFrame f=new JFrame(“Simple Swing Application”);         
        Container contentPane=f.getContentPane();
        contentPane.setLayout(new GridLayout(2,1));
        JButton button=new JButton("Click me");
         final JLabel label=new JLabel();
         contentPane.add(button);//添加按钮
         contentPane.add(label);//添加标签
        button.addActionListener( new ActionListener(){
            public void actionPerformed(ActionEvent event) {                    
               String information=JOptionPane.showInputDialog("请输入一串字符");
               label.setText(information);
            }  }  );
        f.setSize(200,100);//设置大小
        f.show();//显示
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}</code></pre>



<h2 class="wp-block-heading">9.5 Swing的特点和概念</h2>



<p class="is-style-default">顶层容器 Swing提供三个顶层容器的类 </p>



<ol class="wp-block-list"><li>JFrame 实现单个主窗口 </li><li>JDialog  实现一个二级窗口(对话框) </li><li>JApplet 在浏览器窗口中实现一个applet显示区域</li></ol>



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



<h3 class="wp-block-heading">Application和Applet的区别</h3>



<figure class="wp-block-table"><table><tbody><tr><td>比较项目</td><td>Applet</td><td>Application</td></tr><tr><td>程序结构</td><td>init()初始设置；<br>start()开始运行；<br>stop()停止；<br>destory()内存清理；<br>paint()绘图处理；</td><td>由main()开始<br>在构造函数中初始化设置</td></tr><tr><td>开发工具</td><td>javac编译，appletviewer或浏览器查看</td><td>javac编译  java运行字节码文件</td></tr><tr><td>运行环境</td><td>浏览器上</td><td>JRE中</td></tr><tr><td>传播方式   </td><td>HTML格式网页或APP传送</td><td>和JRE发布</td></tr><tr><td>图形界面</td><td>必要</td><td>非必要</td></tr><tr><td>变量输入</td><td>嵌入Web文件中</td><td>命令行输入</td></tr><tr><td>应用范围</td><td>网页</td><td>服务器</td></tr></tbody></table></figure>



<p class="is-style-iw-2em"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3395/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3395</post-id>	</item>
		<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>Spring AOP 通知类型实现</title>
		<link>https://www.cztcode.com/2020/3082/</link>
					<comments>https://www.cztcode.com/2020/3082/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sun, 12 Jul 2020 13:02:44 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3082</guid>

					<description><![CDATA[spring aop通知(advice)分成五类： 前置通知[Before advice]：在连接点前面执行，前置通知不会影响连接点的执行，除非此处抛出异常。 后置通知[After returning advice]：在连接点正常执行完成后执行，如果连接点抛出异常，则不会执行。 异常通知[After throwing advice]：在连接点抛出异常后执行。 返回通知[After (finally [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<h4 class="wp-block-heading">spring aop通知(advice)分成五类：</h4>



<ol class="wp-block-list"><li>前置通知[Before advice]：在连接点前面执行，前置通知不会影响连接点的执行，除非此处抛出异常。</li><li>后置通知[After returning advice]：在连接点正常执行完成后执行，如果连接点抛出异常，则不会执行。</li><li>异常通知[After throwing advice]：在连接点抛出异常后执行。</li><li>返回通知[After (finally) advice]：在连接点执行完成后执行，不管是正常执行完成，还是抛出异常，都会执行返回通知中的内容。</li><li>环绕通知[Around advice]：环绕通知围绕在连接点前后，比如一个方法调用的前后。这是最强大的通知类型，能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。</li></ol>



<p class="is-style-iw-2em">接下来通过编写示例程序来测试一下五种通知类型：</p>



<h3 class="wp-block-heading">非注解方式</h3>



<h4 class="wp-block-heading">前置通知</h4>



<p class="is-style-iw-2em">定义aop类</p>



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



<ul class="wp-block-list"><li>method：被调用的方法，就是连接点</li><li>objects：被调用方法的参数</li><li>o：调用被调用方法的对象</li></ul>



<p class="is-style-iw-2em">xml文件</p>



<p class="is-style-iw-2em">注意要先在ioc容器中放入对象才能使用id引用。</p>



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



<pre class="wp-block-code"><code> &lt;bean id="logBefore" class="com.cztcode.spring.aop.LogBefore"/>
    &lt;aop:config>
        &lt;aop:pointcut id="point" expression="execution(public void com.cztcode.spring.impl.StudentServiceImpl.addStudent(com.cztcode.spring.entity.Student))"/>
&lt;!--        关联ref，当执行点时，执行advice-->
        &lt;aop:advisor advice-ref="logBefore" pointcut-ref="point"/> 
    &lt;/aop:config></code></pre>



<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-72.png" alt="" class="wp-image-3085"/></figure>



<h4 class="wp-block-heading">后置通知</h4>



<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-73.png" alt="" class="wp-image-3086"/></figure>



<p class="is-style-iw-2em">多了一个</p>



<ul class="wp-block-list"><li>returnValue：方法返回值</li></ul>



<p class="is-style-iw-2em">xml文件</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-74-1024x126.png" alt="" class="wp-image-3087"/></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-75.png" alt="" class="wp-image-3088"/></figure>



<h4 class="wp-block-heading">异常通知</h4>



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



<p class="is-style-iw-2em">Exception：异常类型</p>



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



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



<h4 class="wp-block-heading">环绕通知</h4>



<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-81-1024x286.png" alt="" class="wp-image-3095"/></figure>



<p class="is-style-iw-2em">xml文件配置方法相同。</p>



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



<p class="is-style-iw-2em">如果invocation.proceed()没有写，那么这个切入点方法就不会被执行。但是环绕方法的通知会执行。</p>



<p class="is-style-iw-2em">环绕通知底层通过拦截器实现。</p>



<h4 class="wp-block-heading">注解形式</h4>



<h4 class="wp-block-heading">前置和后置通知</h4>



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



<p class="is-style-iw-2em">开启扫描器和对AOP注解支持</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-84.png" alt="" class="wp-image-3098"/></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-85.png" alt="" class="wp-image-3099"/></figure>



<h4 class="wp-block-heading">异常通知</h4>



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



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-87.png" alt="" class="wp-image-3101"/></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-91-1024x109.png" alt="" class="wp-image-3106"/></figure>



<p class="is-style-iw-2em">若发生异常通知，后置通知将不会触发</p>



<h4 class="wp-block-heading">环绕通知</h4>



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



<h4 class="wp-block-heading">最终通知</h4>



<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-89-1024x81.png" alt="" class="wp-image-3104"/></figure>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-90.png" alt="" class="wp-image-3105"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3082/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3082</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>
	</channel>
</rss>
