书名:Java EE实战精粹——MyBatis+Spring+Spring MVC
ISBN:978-7-115-51902-3
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 高洪岩
责任编辑 陈聪聪
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书主要讲解Java EE框架MyBatis、Spring和SpringMVC的核心开发技术,帮助读者进行“精要”式的学习和项目实战,同时汲取Java EE的思想,并最终将其灵活运用到实际工作中。
全书内容共7章,分别对Mybatis、Spring和Spring MVC的基础知识与核心技术实现进行了详细的描述。书中利用大量篇幅介绍了Spring中的DI与AOP,这两种技术是Spring框架的内部原理,掌握它们是学习Spring的重中之重。另外,本书还系统解析了MyBatis+Spring+SpringMVC框架的整合,并介绍了如何使用Spring Boot开发Web软件项目。
本书适用于已具有一定Java编程基础的读者,包括具有Servlet编程经验,以及在Java平台下进行各类软件开发的开发人员、测试人员等。
从用人单位对信息技术(IT)人才招聘的要求来看,越来越趋向于“实战性”,也就是要求员工进入软件公司后能立即融入开发的任务中,快速为软件公司创造巨大的经济利益。本书就本着这个出发点来进行设计。
内容精悍而不失实用价值的主流Java EE开源框架图书,应具有包含主流框架中相当重要且核心、常用的内容,而不是那些无用的知识,这样读者就可以快速上手,并沿着这个核心探索出一些方向,自行在工作和学习中不断拓展和挖掘。这就是作者撰写本书的主要目的。
Java EE的世界非常庞大,以至于世界上没有任何一本书能把它讲得非常完整或详细。要想学好Java语言或Java EE框架并掌握其中丰富的编码技巧,以及设计模式、代码优化,并将它们综合地熟练应用在软件项目中并没有捷径,只有从零开始练习。
本书章节的编排不但涵盖了学习主流Java EE框架所需掌握的核心技术,也涵盖了使用它们进行项目实战的必备知识,主旨就是希望让读者尽快上手,掌握开源Java EE框架的核心内容,正确进行项目实战,汲取Java EE的思想,并最终将这种思想活用到实际工作中。
现在主流的Java EE框架是SSM或Spring Boot,但Spring Boot框架仅仅就是一个“盒子”、一个“封装器”。想要实现功能还是需要整合其他第三方的框架,比如MyBatis、Spring或Spring MVC等,因此,在学习Spring Boot之前必须要有SSM框架的开发经验,那么能让一位Java EE框架初学者从零开始到最终掌握这几个框架,一直是作者的写作目标。有些Java EE开源的框架的确能非常大地提高开发效率,但因为使用的人不多,所以覆盖面比较窄。而软件公司在招聘时的技术需求大多数情况下却是“大众化”的,这就要求应聘者在面试前就有主流Java EE框架的学习或使用经验。如果找不到合适的教材,读者在学习某一项技术时就根本摸不清哪些知识点是常用的,哪些是不常用的,大大降低学习效率,分散了注意力。
首先,本书适合所有Java程序员,作为Java开源世界的主流框架,Java EE程序员没有理由不学习它们。其次,本书适合希望学习这些框架编程的在校学生。学校的功课很多,而一本大部头的框架书籍需要花费大量的时间研读,因此在学习效率上,本书可以快速带领读者进入Java EE框架开发的殿堂,同时又不会遗漏掉应该掌握的核心技能。
第1章将会介绍基于SQL映射的MyBatis框架,在本章中,读者可以使用此框架操作主流的数据库,并学习MyBatis核心API的使用,采用自定义封装法来简化MyBatis的操作代码,进而提高开发效率。
第2章主要讲解了有关MyBatis映射的知识,包括<sql>、<resultMap>、<choose>、<set>、<foreach>等常用标签,DB连接信息存储到Properties文件的读取,使用JDBC数据源,别名typeAliases的配置,CLOB字段的读取以及分页等必备技术点。
第3章和第4章开始介绍Spring中的IOC和AOP技术,包含注入、注入原理、动态代理的实现与AOP切面的原理。
第5章将介绍时下流行的Spring MVC框架,读者可体会使用此框架开发一个经典登录功能时使用的技术点,限制form提交方式,还要掌握分组分模块开发的技术,重定向/转发的使用,JSON+AJAX+Spring MVC联合开发,上传/下载的实现,以及数据验证功能的使用,XML配置文件的处理,业务层Service的注入,ModelAndView对象的使用,以及HttpSession在Spring MVC中的使用等功能。
第6章展现了特别常用的MyBatis+Spring+Spring MVC整合,以当前极具实战的组合框架来讲解整合的过程,而不囿于某一个框架本身,整合后的项目代码写法更加统一,便于维护与扩展。
第7章完整介绍了使用Spring Boot开发Web软件项目的过程。通过将Spring MVC与MyBatis、Spring Boot进行整合,带领读者学以致用。
要学好Java EE,不但要有较好的Java基础,还需要了解多项开发框架的技术,因为Java EE框架本身就是采用这些技术开发出来的。
首先需要声明,本书不是Java Web的入门教程,学习本书之前先要对Java Web中的JSP、Servlet等Web技术有所了解,尽量能完整地使用JSP或Servlet开发一个小型项目,再阅读本书,读者会发现代码的分层更加明确,结构更加清楚。
软件开发实践才是硬道理,设计、排错、拥有更多想法的经验非常重要,因此,请读者使用手中的键盘,练习一下吧!
尽量在读者自己的计算机中动手执行本书的所有代码。仅仅看书和自己动手存在天壤之别,动手运行代码和看书结合起来才能更深地理解框架的各项功能。
为了环保,本书不提供光盘,读者可以从异步社区下载本书配套资源。
由于Java EE内容涵盖面广,涉及的知识点非常多,加之作者水平有限,错误之处在所难免,恳请各位读者批评指正。可以通过QQ:279377921与作者联系,期待与读者进行技术上的交流。
在本书出版的过程中,得到公司领导和同事的大力支持,在此表示感谢;感谢家人给予我充足的时间来撰写本书;感谢出生两个多月的儿子高晟京,看到你,我更加有了动力;最后感谢在本书上耗费大量精力的各位编辑,编辑们在工作上仔细、谨慎的工作态度值得我学习。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
本书提供如下资源:
要获得以上配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。
如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
本章目标:
了解MyBatis核心对象的生命周期
使用SqlSession对象或Mapper接口操作数据库
MyBatis结合ThreadLocal类进行CURD的封装
手动搭建开发环境
MyBatis是一个操作数据库的框架,那什么是框架?框架就是软件功能的半成品,框架提供了一个软件项目中通用的功能,将大多数常见的功能进行封装,无须自己重复开发,框架提高了开发及运行效率。在软件公司中,大多数情况是使用框架开发软件项目。
MyBatis是一个基于“ORM”的框架,ORM的全称是对象关系映射(Object Relational Mapping)。
对象(Object)就是Java中的对象,关系(Relational)就是数据库中的数据表,基于“ORM”的框架是把数据在对象和关系之间进行双向转换。
ORM的细节可以从3个方面来介绍。
(1)1个类对应1个表。
(2)1个类的对象对应表中的1行。
(3)1个类的对象中的属性对应1个表中的列。
ORM映射关系如图1-1所示。
MyBatis框架可以将Java类中的数据转化成数据表中的记录,或者将数据表中的记录封装到Java类中,过程如图1-2所示。
图1-1 ORM映射关系
图1-2 ORM框架映射的主流程
从图1-2的过程来看,程序员不再直接使用JDBC对象访问数据库,而是以面向对象的方式来使用实体类,对实体类进行的增加、删除、更新和查询操作都会由ORM框架转化成对数据库的增加、删除、更新和查询操作。
ORM框架内部的核心技术的原理其实就是JDBC和反射,这个技术是由ORM框架,也就是MyBatis来进行封装的。
一个事物一定要有优势才不会被淘汰,软件技术也是如此。
MyBatis是现阶段操作数据库的主流框架,此框架的主要作用就是更加便捷地操作数据库。它具有很多优势,和程序员密切相关的优势主要体现在如下7个方面。
(1)ROW行与Entity实体类双向转换:可以将数据表中的ROW行与Entity实体类进行互相转换,比如将ResultSet对象返回的数据自动封装到Entity实体类或List中,或将Entity实体类中的数据转换成数据表中新的一行ROW。
(2)SQL语句与Java文件分离:可以把SQL语句写到XML文件中,目的是将SQL语句与Java文件进行分离,有利于代码的后期维护,也可使代码的分层更加明确。
(3)允许对SQL语句进行自定义优化:因为MyBatis框架是使用SQL语句对数据库进行操作的,所以可以单独地对SQL语句进行优化,以提高操作效率。而Hibernate框架却做不到这一点,所以MyBatis相比Hibernate框架就具有很大的优势,这也是现阶段大部分软件公司逐步用MyBatis替换掉Hibernate框架的主要原因。
(4)减化DAO层代码:使用传统的JDBC开发方式时,需要写上必要的DAO层代码以对数据库进行操作,但这样的代码写法在软件开发的过程中非常不便,因为多个DAO类中的大部分JDBC代码是冗余的,所以MyBatis解决了这个问题。使用MyBatis做查询时可以自动将数据表中的数据记录封装到实体类或Map中,再将它们放入List进行返回。这么常见而且有利于提高开发效率的功能MyBatis都可以自由方便地处理,不需要程序员写底层的实现代码,MyBatis就可以完全进行封装。从此观点来看,使用MyBatis框架去开发软件非常方便、快捷,省略了大量冗余的JDBC代码,MyBatis把常用的JDBC操作都进行了封装,进而提高开发效率,MyBatis框架很有使用上的必要性。
(5)半自动化所带来的灵活性:MyBatis是“半自动化”的“ORM”框架,但它应该算作SQL映射框架(SQL Mapper Framework)。将MyBatis称为“半自动化的ORM框架”是因为MyBatis操作数据库时还是使用原始的SQL语句,这些SQL语句还需要程序员自己来进行设计,这就是半自动化。MyBatis在使用方式上和全自动化的ORM框架Hibernate有着非常大的区别,MyBatis是以SQL语句为映射基础,而Hibernate是彻底地基于实体类与表进行映射,基本是属于全自动化的ORM框架。但正是因为MyBatis属于半自动化的ORM框架这个特性,所以可以将SQL语句灵活多变的特性融入项目开发中。
(6)支持XML或Annotations注解的方式进行ORM:MyBatis可以使用XML或Annotations注解的方式将数据表中的记录映射成1个Map或Java POJO实体类对象,但推荐使用XML方式,该方式也是MyBatis官方推荐的。
(7)功能丰富:MyBatis还可以实现定义SQL段落、调用存储过程和进行高级映射等功能。
本章将探究ORM框架的底层原理,用代码来模拟实现一个微型的ORM功能。
MyBatis实现ROW行与Entity实体类双向转换的原理是基于JDBC和反射技术,MyBatis框架只是对JDBC技术进行了轻量级的封装,使程序员更方便地去操作数据库。
为了对MyBatis框架核心功能的原理有更加细致的了解,在本节就来实现一下ROW行与Entity实体类的双向转换。
创建测试用的项目entity-row-double。
创建获得Connection连接对象,代码如下:
package dbtools;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class GetConnection {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
String driverName = "oracle.jdbc.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String username = "y2";
String password = "123";
Class.forName(driverName);
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
}
创建实体类Userinfo,核心代码如下:
public class Userinfo {
private long id;
private String username;
private String password;
public Userinfo() {
}
public Userinfo(long id, String username, String password) {
super();
this.id = id;
this.username = username;
this.password = password;
}
//省略get和set方法
}
创建泛型DAO类BaseDAO,代码如下:
package dao;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import dbtools.GetConnection;
public class BaseDAO<T> {
// 此方法模拟了MyBatis的save()方法
// MyBatis框架内部的核心和本示例基本一样
// 使用的技术就是JDBC和反射
public void save(T t)
throws IllegalArgumentException, IllegalAccessException,
ClassNotFoundException, SQLException {
String sql = "insert into ";
String colName = "";
String colParam = "";
String begin = "(";
String end = ")";
Class classRef = t.getClass();
String tableName = classRef.getSimpleName().toLowerCase();
sql = sql + tableName;
List values = new ArrayList();
Field[] fieldArray = classRef.getDeclaredFields();
for (int i = 0; i < fieldArray.length; i++) {
Field eachField = fieldArray[i];
eachField.setAccessible(true);
String eachFieldName = eachField.getName();
Object eachValue = eachField.get(t);
colName = colName + "," + eachFieldName;
colParam = colParam + ",?";
values.add(eachValue);
}
colName = colName.substring(1);
colParam = colParam.substring(1);
sql = sql + begin + colName + end;
sql = sql + " values" + begin + colParam + end;
System.out.println(sql);
for (int i = 0; i < values.size(); i++) {
System.out.println(values.get(i));
}
Connection conn = GetConnection.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < values.size(); i++) {
ps.setObject(i + 1, values.get(i));
}
ps.executeUpdate();
ps.close();
conn.close();
}
// 此方法模拟了MyBatis的get()方法
public T get(Class<T> classObject, long id)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
T t = null;
String sql = "select * from " + classObject.getSimpleName().toLowerCase() + " where id=?";
Connection conn = GetConnection.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
t = classObject.newInstance();
Field[] fieldArray = classObject.getDeclaredFields();
for (int i = 0; i < fieldArray.length; i++) {
Field eachField = fieldArray[i];
eachField.setAccessible(true);
String fieldName = eachField.getName();
Object value = rs.getObject(fieldName);
if (value.getClass().getTypeName().equals("java.math.BigDecimal")) {
long longValue = Long.parseLong("" + value);
eachField.set(t, longValue);
} else {
eachField.set(t, value);
}
}
}
rs.close();
ps.close();
conn.close();
return t;
}
// 此方法模拟了MyBatis的update()方法
public void update(T t)
throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException, SQLException {
String sql = "update " + t.getClass().getSimpleName().toLowerCase() + " set ";
String whereSQL = " where id=?";
String colName = "";
Class classRef = t.getClass();
List values = new ArrayList();
Field[] fieldArray = classRef.getDeclaredFields();
long idValue = 0;
for (int i = 0; i < fieldArray.length; i++) {
Field eachField = fieldArray[i];
eachField.setAccessible(true);
String eachFieldName = eachField.getName();
if (!eachFieldName.equals("id")) {
Object eachValue = eachField.get(t);
colName = colName + "," + eachFieldName + "=?";
values.add(eachValue);
} else {
Object eachValue = eachField.get(t);
idValue = Long.parseLong(eachValue.toString());
}
}
values.add(idValue);
colName = colName.substring(1);
sql = sql + colName + whereSQL;
System.out.println(sql);
for (int i = 0; i < values.size(); i++) {
System.out.println(values.get(i));
}
Connection conn = GetConnection.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < values.size(); i++) {
ps.setObject(i + 1, values.get(i));
}
ps.executeUpdate();
ps.close();
conn.close();
}
// 此方法模拟了MyBatis的delete()方法
public void delete(T t) throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException,
SQLException, NoSuchFieldException, SecurityException {
String sql = "delete from " + t.getClass().getSimpleName().toLowerCase();
String whereSQL = " where id=?";
Class classRef = t.getClass();
List values = new ArrayList();
Field idField = classRef.getDeclaredField("id");
idField.setAccessible(true);
Object object = idField.get(t);
values.add(object);
sql = sql + whereSQL;
System.out.println(sql);
for (int i = 0; i < values.size(); i++) {
System.out.println(values.get(i));
}
Connection conn = GetConnection.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < values.size(); i++) {
ps.setObject(i + 1, values.get(i));
}
ps.executeUpdate();
ps.close();
conn.close();
}
}
增加记录的代码如下:
public class Insert {
public static void main(String[] args)
throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException, SQLException {
Userinfo userinfo = new Userinfo();
userinfo.setId(1000L);
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
BaseDAO<Userinfo> dao = new BaseDAO<>();
dao.save(userinfo);
}
}
查询记录的代码如下:
public class Select {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
BaseDAO<Userinfo> dao = new BaseDAO<>();
Userinfo userinfo = dao.get(Userinfo.class, 1000L);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword());
}
}
修改记录的代码如下:
public class Update {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
BaseDAO<Userinfo> dao = new BaseDAO<>();
Userinfo userinfo = dao.get(Userinfo.class, 1000L);
userinfo.setUsername("xxx");
userinfo.setPassword("xxxxxx");
dao.update(userinfo);
}
}
删除记录的代码如下:
public class Delete {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException,
ClassNotFoundException, SQLException, IllegalArgumentException,
NoSuchFieldException, SecurityException {
BaseDAO<Userinfo> dao = new BaseDAO<>();
Userinfo userinfo = dao.get(Userinfo.class, 1000L);
dao.delete(userinfo);
}
}
以上代码的作用就是使用JDBC结合反射技术来将数据表中的1行记录和实体类进行双向转换,这也是ORM框架的原理。以上代码用到了JDBC(结合)反射技术,MyBatis实现ORM的原理就是JDBC和反射技术。
提到标记语言人们就容易想起HTML。HTML提供了很多标签来实现Web前端界面的设计,但HTML中的标签并不允许自定义。如果想定义一些独有的标签,HTML就不再可行了,这时可以使用XML来实现。
XML的全称是eXtensible Markup Language(可扩展标记语言),它可以自定义标记名称与内容,在灵活度上相比HTML有大幅提高,经常用在配置以及数据交互领域。
在开发软件项目时,经常会接触XML文件,比如web.xml文件中就有XML代码,XML代码的主要作用就是配置,那么在Java中如何读取XML中的内容呢?
创建名称为xmlTest的Java项目,在项目中引入dom4j-1.6.1.jar文件,创建struts.xml文件,代码如下:
<mymvc>
<actions>
<action name="list" class="controller.List">
<result name="toListJSP">
/list.jsp
</result>
<result name="toShowUserinfoList" type="redirect">
showUserinfoList.ghy
</result>
</action>
<action name="showUserinfoList" class="controller.ShowUserinfoList">
<result name="toShowUserinfoListJSP">
/showUserinfoList.jsp
</result>
</action>
</actions>
</mymvc>
创建Reader类,代码如下:
package test;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class Reader {
public static void main(String[] args) {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(reader.getClass()
.getResourceAsStream("/struts.xml"));
Element mymvcElement = document.getRootElement();
System.out.println(mymvcElement.getName());
Element actionsElement = mymvcElement.element("actions");
System.out.println(actionsElement.getName());
System.out.println("");
List<Element> actionList = actionsElement.elements("action");
for (int i = 0; i < actionList.size(); i++) {
Element actionElement = actionList.get(i);
System.out.println(actionElement.getName());
System.out.print("name="
+ actionElement.attribute("name").getValue());
System.out.println("action class="
+ actionElement.attribute("class").getValue());
List<Element> resultList = actionElement.elements("result");
for (int j = 0; j < resultList.size(); j++) {
Element resultElement = resultList.get(j);
System.out.print(" result name="
+ resultElement.attribute("name").getValue());
Attribute typeAttribute = resultElement.attribute("type");
if (typeAttribute != null) {
System.out.println(" type=" + typeAttribute.getValue());
} else {
System.out.println("");
}
System.out.println(" " + resultElement.getText().trim());
System.out.println("");
}
System.out.println("");
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
程序运行后的结果如图1-3所示。
图1-3 用dom4j解析的XML文件内容
上面的示例是读取解析XML文件,那么如何创建XML文件呢?继续创建一个名称为createXML的Java项目,并创建Java类Writer,代码如下:
package test;
import java.io.FileWriter;
import java.io.IOException;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
public class Writer {
public static void main(String[] args) {
try {
Document document = DocumentHelper.createDocument();
Element mymvcElement = document.addElement("mymvc");
Element actionsElement = mymvcElement.addElement("actions");
// /
Element listActionElement = actionsElement.addElement("action");
listActionElement.addAttribute("name", "list");
listActionElement.addAttribute("class", "controller.List");
Element toListJSPResultElement = listActionElement
.addElement("result");
toListJSPResultElement.addAttribute("name", "toListJSP");
toListJSPResultElement.setText("/list.jsp");
Element toShowUserinfoListResultElement = listActionElement
.addElement("result");
toShowUserinfoListResultElement.addAttribute("name",
"toShowUserinfoList");
toShowUserinfoListResultElement.addAttribute("type", "redirect");
toShowUserinfoListResultElement.setText("showUserinfoList.ghy");
// /
Element showUserinfoListActionElement = actionsElement
.addElement("action");
showUserinfoListActionElement.addAttribute("name",
"showUserinfoList");
showUserinfoListActionElement.addAttribute("class",
"controller.ShowUserinfoList");
Element toShowUserinfoListJSPResultElement = showUserinfoListActionElement
.addElement("result");
toShowUserinfoListJSPResultElement.addAttribute("name",
"toShowUserinfoListJSP");
toShowUserinfoListResultElement.setText("/showUserinfoList.jsp");
// /
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileWriter("ghy.xml"), format);
writer.write(document);
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
程序运行后在当前项目中创建名称为ghy.xml的文件,文件内容如图1-4所示。
图1-4 创建的ghy.xml中的内容
上面的示例是创建XML文件,那么如何修改XML文件呢?继续创建一个名称为Update的Java类,代码如下:
package dom4jTest;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
public class Update {
public static void main(String[] args) throws IOException {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(reader.getClass().getResourceAsStream("/test.xml"));
Element mymvcElement = document.getRootElement();
Element actionsElement = mymvcElement.element("actions");
List<Element> actionList = actionsElement.elements("action");
for (int i = 0; i < actionList.size(); i++) {
Element actionElement = actionList.get(i);
List<Element> resultList = actionElement.elements("result");
for (int j = 0; j < resultList.size(); j++) {
Element resultElement = resultList.get(j);
String resultName = resultElement.attribute("name").getValue();
if (resultName.equals("toShowUserinfoList")) {
Attribute typeAttribute = resultElement.attribute("type");
if (typeAttribute != null) {
typeAttribute.setValue("zzzzzzzzzzzzzzzzzzzzzz");
resultElement.setText("xxxxxxxxxxxxxxxxxxx");
}
}
}
}
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileWriter("src\\ghy.xml"), format);
writer.write(document);
writer.close();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
产生的XML文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<mymvc>
<actions>
<action name="list" class="controller.List">
<result name="toListJSP">/list.jsp</result>
<result name="toShowUserinfoList" type="zzzzzzzzzzzzzzzzzzzzzz">xxxxxxxxxxxxxxxxxxx</result>
</action>
<action name="showUserinfoList" class="controller.ShowUserinfoList">
<result name="toShowUserinfoListJSP">/showUserinfoList.jsp</result>
</action>
</actions>
</mymvc>
成功更改XML文件中的属性值与文本内容。
那么如何删除XML中的Node节点呢?创建Delete.java,代码如下:
package dom4jTest;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
public class Delete {
public static void main(String[] args) throws IOException {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(reader.getClass().getResourceAsStream("/test.xml"));
Element mymvcElement = document.getRootElement();
Element actionsElement = mymvcElement.element("actions");
List<Element> actionList = actionsElement.elements("action");
for (int i = 0; i < actionList.size(); i++) {
Element actionElement = actionList.get(i);
List<Element> resultList = actionElement.elements("result");
Element resultElement = null;
boolean isFindNode = false;
for (int j = 0; j < resultList.size(); j++) {
resultElement = resultList.get(j);
String resultName = resultElement.attribute("name").getValue();
if (resultName.equals("toShowUserinfoList")) {
isFindNode = true;
break;
}
}
if (isFindNode == true) {
actionElement.remove(resultElement);
}
}
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileWriter("src\\ghy.xml"), format);
writer.write(document);
writer.close();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
产生的XML文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<mymvc>
<actions>
<action name="list" class="controller.List">
<result name="toListJSP">/list.jsp</result>
</action>
<action name="showUserinfoList" class="controller.ShowUserinfoList">
<result name="toShowUserinfoListJSP">/showUserinfoList.jsp</result>
</action>
</actions>
</mymvc>
成功删除Node节点。
那么如何删除属性Attr呢?创建DeleteAttr类,代码如下:
package dom4jTest;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
public class DeleteAttr {
public static void main(String[] args) throws IOException {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(reader.getClass().getResourceAsStream("/test.xml"));
Element mymvcElement = document.getRootElement();
Element actionsElement = mymvcElement.element("actions");
List<Element> actionList = actionsElement.elements("action");
for (int i = 0; i < actionList.size(); i++) {
Element actionElement = actionList.get(i);
List<Element> resultList = actionElement.elements("result");
for (int j = 0; j < resultList.size(); j++) {
Element resultElement = resultList.get(j);
String resultName = resultElement.attribute("name").getValue();
if (resultName.equals("toShowUserinfoList")) {
Attribute typeAttribute = resultElement.attribute("type");
if (typeAttribute != null) {
resultElement.remove(typeAttribute);
}
}
}
}
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileWriter("src\\ghy.xml"), format);
writer.write(document);
writer.close();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
产生的XML文件代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<mymvc>
<actions>
<action name="list" class="controller.List">
<result name="toListJSP">/list.jsp</result>
<result name="toShowUserinfoList">showUserinfoList.ghy</result>
</action>
<action name="showUserinfoList" class="controller.ShowUserinfoList">
<result name="toShowUserinfoListJSP">/showUserinfoList.jsp</result>
</action>
</actions>
</mymvc>
成功删除XML文件中的type属性。
本节将搭建MyBatis的开发环境,为后面的学习做好准备工作。
进入开发工具Eclipse的官方网址并进入下载页面,单击“Download Packages”链接下载“Eclipse IDE for Java EE Developers”,也就是64位版本的Eclipse,如图1-5所示。
图1-5 单击“Download Packages”链接下载Eclipse
下载成功后,将Eclipse解压到C:\MyBatisEclipse路径中。
由于MyBatis是第三方的框架,javaee.jar中并不包含它的API,因此需要单独进行下载。进入GitHub官网后找到MyBatis项目,打开网页后看到如图1-6所示的界面。
图1-6 MyBatis项目页面
单击“Download Latest”链接后打开下载页面,如图1-7所示。
图1-7 准备下载MyBatis框架
下载MyBatis框架压缩包mybatis-3.4.6.zip以及源代码Source code(zip)文件。
解压mybatis-3.4.6.zip文件可以看到MyBatis相关的资料,比如MyBatis的核心JAR包mybatis-3.4.6.jar和PDF格式的帮助文档mybatis-3.4.6.pdf,文件夹lib中存放的是mybatis-3.4.6.jar依赖的其他JAR包文件,内容如图1-8所示。
图1-8 MyBatis压缩包中的资料
把mybatis-3.4.6.jar和ojdbc8.jar驱动文件复制到lib 文件夹中,其中一共有14个JAR包文件,总大小约为10MB,如图1-9所示。
图1-9 所有的JAR包文件
在后面的学习中需要在Eclipse里创建很多Java项目,如果在每个Java项目中都复制约10MB大小的JAR包文件,则非常占用硬盘空间。解决的办法就是在Eclipse中创建一个Library库,向这个Library库中添加14个JAR包文件,然后让每个Java项目引用这个Library库即可,这样做就大大降低了硬盘占用率。
进入Eclipse,在“Preferences”界面中依次选择“Java”→“Build Path”→“User Libraries”界面,如图1-10所示。
图1-10 进入User Libraries界面
单击“New…”按钮创建Library库,弹出界面如图1-11所示。
图1-11 设置Library库的名称
输入Library库的名称为“MyBatisJAR”,单击“OK”按钮,再单击“Add External JARs…”按钮添加扩展的JAR包,如图1-12所示。
扩展JAR包文件添加成功后再单击右下角的“Apply and Close”按钮完成创建Library库,如图1-13所示。
图1-12 单击“Add External JARs…”按钮添加扩展JAR包
图1-13 自定义Library库MyBatisJAR创建完毕
创建名称为libraryTest的Java项目,在创建向导中单击“Add Library…”按钮添加Library库,效果如图1-14所示。
图1-14 单击“Add Library…”按钮添加Library库
在弹出的界面中选择“User Library”选项,如图1-15所示。
选择名称为“MyBatisJAR”的Library库,如图1-16所示。
图1-15 选择“User Library”选项
图1-16 选择名称为“MyBatisJAR”的Library库
单击“Finish”按钮后可以发现项目引用了MyBatisJAR库,如图1-17所示。
图1-17 成功引入MyBatisJAR库
再单击“Finish”按钮完成Java项目的创建。
Java项目结构如图1-18所示。
图1-18 创建完成的Java项目结构
在新建的Java类中就可以使用mybatis.jar包中的类来操作数据库了。
开门见山,是快速学习一门技术的优选方式。
MyBatis框架的核心是SqlSessionFactoryBuilder、SqlSessionFactory和SqlSession对象,这三者之间的创建关系如下。
SqlSessionFactoryBuilder创建出SqlSessionFactory,SqlSessionFactory创建出SqlSession。
使用SqlSessionFactoryBuilder类创建SqlSessionFactory对象的方式可以来自于一个XML配置文件,还可以来自于一个实例化的 Configuration 对象,因为使用 XML 方式创建SqlSessionFactory对象在使用上比较广泛,而且也是官方所推荐的,所以在下面的小节就进行此实验。
使用SqlSessionFactoryBuilder类创建SqlSessionFactory对象可以来自于一个XML配置文件,这个XML配置文件的代码模板如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"mybatis-3-config.dtd">
<configuration>
<properties>
<property name="" value="" />
</properties>
<settings>
<setting name="" value="" />
</settings>
<typeAliases>
<typeAlias type="" />
<package name="" />
</typeAliases>
<typeHandlers>
<typeHandler handler="" />
<package name="" />
</typeHandlers>
<objectFactory type="">
</objectFactory>
<objectWrapperFactory type=""></objectWrapperFactory>
<reflectorFactory type="" />
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<environments default="">
<environment id="">
<transactionManager type="" />
<dataSource type="">
<property name="" value="" />
</dataSource>
</environment>
</environments>
<databaseIdProvider type="">
<property name="" value="" />
</databaseIdProvider>
<mappers>
<mapper />
<package name="" />
</mappers>
</configuration>
上面的代码就是MyBatis配置文件的模板,但不要对上面模板中冗长的代码产生不安,因为在初期学习的过程中并不会使用上面全部的配置标记。另外,也不要死记硬背模板中的代码,因为在PDF帮助文档中提供了一个极简版模板,复制那个极简版的XML模板代码就可以搭建MyBatis的开发环境。
注意:
模板中标记的顺序不能改变,不然会出现异常。
SqlSessionFactory对象存储MyBatis环境的全局信息。
创建名称为mybatis1的Java项目。
根据XML配置文件来创建SqlSessionFactory对象的核心代码,具体如下:
public class Test1 {
public static void main(String[] args) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build
(inputStream);
System.out.println(sqlSessionFactory);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在代码中使用Resources类将mybatis-config.xml文件转换成InputStream输入流,再把InputStream输入流传入SqlSessionFactoryBuilder类的build()方法里来创建SqlSessionFactory对象。
类SqlSessionFactoryBuilder的主要作用就是根据mybatis-config.xml配置文件中的信息来创建SqlSessionFactory对象。
配置文件mybatis-config.xml的代码及解释如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--在配置文件中可以有多个<environment id="development">配置 -->
<!--目的就是在配置文件中保存多个数据库的连接信息 -->
<!--而使用<environments default="development">代码的作用就是 -->
<!--默认使用id为development的数据库连接 -->
<environments default="development">
<!--定义id为development的数据库连接 -->
<environment id="development">
<!-- 定义数据库的事务要由程序员的代码进行控制 -->
<transactionManager type="JDBC" />
<!-- 使用MyBatis自己提供的连接池来处理Connection对象 -->
<dataSource type="POOLED">
<!-- 连接数据库的4大变量 -->
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
</configuration>
前面小节介绍过,MyBatis配置文件中的代码是不需要死记硬背的,因此,上面mybatis- config.xml配置文件中的代码来自于PDF帮助文档并少量更改,PDF帮助文档中的XML配置文件模板代码示例如图1-19所示。
配置文件mybatis-config.xml的主要作用就是定义如何连接数据库,包含连接数据库所用到的username、password及url等参数,但在本实验中,文件mybatis-config.xml里并没有实质的属性值,而是使用${xxxx}作为替代。这是因为在获取SqlSessionFactory工厂对象时,不需要提供这些具体的参数值。
图1-19 帮助文档提供的XML配置文件的极简代码
程序运行后并没有出现异常,控制台打印的信息如下:
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@1554909b
DefaultSqlSessionFactory.java是SqlSessionFactory.java接口的实现类,具有实现关系,效果如图1-20所示。
图1-20 实现关系
到此,使用XML配置文件创建SqlSessionFactory对象是成功的。
对数据库执行增加、删除、更新和查询操作是需要使用SqlSession对象的。使用SqlSessionFactory可以创建SqlSession对象。
示例核心代码如下:
public class Test2 {
public static void main(String[] args) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().
build(inputStream);
System.out.println(sqlSessionFactory);
SqlSession sqlSession = sqlSessionFactory.openSession();
System.out.println(sqlSession);
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
调用SqlSessionFactory对象的openSession()方法来创建SqlSession对象。
程序运行后,在控制台输出的结果如下:
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@1554909b
org.apache.ibatis.session.defaults.DefaultSqlSession@42d3bd8b
获得SqlSession对象并调用下面的4种方法可以对数据库进行操作:
// session.insert(arg0); //增加操作
// session.delete(arg0); //删除操作
// session.update(arg0); //更新操作
// session.selectList(arg0); //查询操作
DefaultSqlSession.java是SqlSession.java接口的实现类,具有实现关系,效果如图1-21所示。
图1-21 实现关系
到此,使用SqlSessionFactory创建SqlSession对象是成功的。
虽然现在正在学习新的知识,包括要熟悉新的类名、新的方法名称、新的包名等,但是MyBatis在API的设计结构上是相当简洁的,大部分是重载的方法。下面来看一看SqlSessionFactoryBuilder类的结构,效果如图1-22所示。
图1-22 SqlSessionFactoryBuilder类结构
再来看一下SqlSessionFactory类的结构,如图1-23所示。
图1-23 SqlSessionFactory类的结构
从图1-22和图1-23中可以看到,基本全是重载的方法,主要就是通过SqlSessionFactory Builder.build()方法取得SqlSessionFactory对象,再使用SqlSessionFactory.openSession()方法取得SqlSession对象。
SqlSession主要的作用是对数据库进行CURD操作。
使用MyBatis操作数据库要使用SQL映射文件和实体类,但这两个文件的代码比较繁杂,尤其是如果数据表的字段很多,则手写实体类将会成为噩梦。此种情况也存在于Hibernate框架中。因此,为了提高开发效率,MyBatis官方提供1个Eclipse插件,名称为MyBatis Generator。该插件主要的功能就是根据数据表结构生成对应的 SQL 映射文件和实体类,此插件需要在Eclipse中在线安装,安装过程如下。
进入Eclipse的Marketplace,效果如图1-24所示。
图1-24 进入Eclipse的Marketplace
搜索关键字“mybatis”后出现了“MyBatis Generator”插件,效果如图1-25所示。
图1-25 搜索到“MyBatis Generator”插件
单击“Install”按钮在线安装,弹出如图1-26所示的界面。
图1-26 接受许可
单击“Finish”按钮开始安装,安装过程如图1-27所示。
在安装过程中会弹出提示,如图1-28所示,信息内容是安装的软件有未签名的内容,询问是否继续,单击“Install anyway”按钮继续安装。
图1-27 安装过程
插件安装完毕后询问是否重新启动Eclipse,如图1-29所示,单击“Restart Now”按钮重新启动Eclipse。
图1-28 询问是否继续安装
图1-29 是否重启Eclipse
重新启动Eclipse后再次进入Eclipse中的Marketplace,进入“Installed”标签页,发现插件MyBatis Generator已经安装成功,效果如图1-30所示。
图1-30 插件MyBatis Generator已经安装成功
插件安装成功后就可以在新建Java项目时创建MyBatis Generator Configuration File.xml文件了,通过这个文件就可以将数据表的信息逆向成SQL映射文件以及实体类,不需要手写代码了。
本节就要使用MyBatis Generator工具逆向的代码操作Oracle数据库。
注意:
如果在逆向的过程中出现如下异常:
那么说明并没有找到JDBC驱动,可以先选择右键菜单项“Run As”→“Run Configurations…”,然后在“Classpath”标签页中添加JDBC驱动。
新建名称为GeneratorOracle的Java项目,并关联MyBatisJAR库。
然后在src节点下单击鼠标右键,新建1个MyBatis的Generator配置文件,如图1-31所示。
C:\spring-tool-suite-3.9.5.RELEASE-workspace\.metadata\.plugins\org.mybatis.generator.eclipse.ui\.generatedAntScripts\GeneratorOracle-generatorConfig.xml.xml:4: java.lang.RuntimeException: Exception getting JDBC Driver
java.lang.RuntimeException: Exception getting JDBC Driver
单击“Next”按钮出现如图1-32所示界面。
图1-31 创建生成ORM的配置XML文件
图1-32 将generatorConfig.xml文件放入src节点下即可
在图1-32界面中不需要更改配置,默认即可,单击“Finish”按钮完成Generator配置文件的创建。
更改generatorConfig.xml的配置文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
driverClass="oracle.jdbc.OracleDriver" password="123" userId="y2" />
<javaModelGenerator targetPackage="entity"
targetProject="GeneratorOracle" />
<sqlMapGenerator targetPackage="sqlmapping"
targetProject="GeneratorOracle" />
<javaClientGenerator targetPackage="client"
targetProject="GeneratorOracle" type="XMLMAPPER" />
<table schema="y2" tableName="userinfo">
</table>
</context>
</generatorConfiguration>
注意:
在连接Oracle数据库时,<table>标记的schema属性值y2是登录Oracle数据库的用户名。
配置文件generatorConfig.xml是MyBatis Generator插件中必备的文件,通过此文件可以将数据表的结构逆向出对应的实体类、SQL映射文件以及客户端代码,使用这些代码就可以对数据表进行增加、删除、更新和查询操作。
数据表userinfo的表结构如图1-33所示。
图1-33 userinfo数据表结构
配置文件generatorConfig.xml准备就绪后,单击图1-34中的菜单项“Run MyBatis Generator”。
图1-34 根据XML配置文件生成SQL映射文件
在控制台输出成功逆向的信息,如图1-35所示。
图1-35 逆向操作成功
成功逆向后的Java项目结构如图1-36所示。
图1-36 项目结构
至此,操作数据库的基础文件已经准备完毕,下面开始在Oracle数据库的userinfo数据表中添加1条记录。
注意:
在mybatis-config.xml配置文件中添加如下配置:
因为包sqlmapping中的文件UserinfoMapper.xml里存储了SQL语句,所以在配置文件中需要关联。
在src中创建mybatis-config.xml文件,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url"
value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="username" value="y2" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/UserinfoMapper.xml" />
</mappers>
</configuration>
<mappers>
<mapper resource="sqlmapping/UserinfoMapper.xml" />
</mappers>
类核心代码如下:
public class Test {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
SqlSession session = factory.openSession();
session.insert("insert", userinfo);
session.commit();
session.close();
}
}
方法main()中的代码是一个经典的insert 数据表的功能,从代码中可以看到MyBatis用相当精简的API就可以完全地控制数据表中的记录。可见MyBatis在学习、开发等方面成本都是比较低的。
程序运行后在控制台输出的异常信息如下:
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: 无法将 NULL 插入 ("Y2"."USERINFO"."ID")
### The error may involve client.UserinfoMapper.insert-Inline
### The error occurred while setting parameters
### SQL: insert into Y2.USERINFO (ID, USERNAME, PASSWORD ) values (?, ?, ? )
### Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: 无法将 NULL 插入 ("Y2"."USERINFO"."ID")
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)
at test.Test.main(Test.java:24)
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: 无法将 NULL 插入 ("Y2"."USERINFO"."ID")
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)
出错信息提示不能对id主键列赋null空值,由于Oracle数据库的主键并不是自增的,在SQL语句中需要对id列进行传值,但本示例中并未对id属性进行显式赋值,并且在SQL语句中也没有使用序列,因此出现了异常。在正常情况下,在UserinfoMapper.xml文件的insert语句中需要结合序列来实现添加记录的功能,这样可以免去对id属性传值的Java代码。逆向生成错误的insert语句如下所示:
<insert id="insert" parameterType="entity.Userinfo">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Mon Sep 10 16:59:40 CST 2018.
-->
insert into Y2.USERINFO (ID, USERNAME, PASSWORD
)
values (#{id,jdbcType=NUMERIC}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}
)
</insert>
上面生成的SQL语句并未使用序列,也并未对id属性传值,因此出现了异常。
程序代码session.insert("insert", userinfo);的第1个参数"insert"就是SQL映射文件UserinfoMapper.xml中的配置<insert id="insert" parameterType="entity.Userinfo">的id值,代表要执行id值为insert的SQL语句,insert()方法的第2个参数的数据类型由以下配置代码来决定:
parameterType="entity.Userinfo"
上面程序在运行时出现异常,说明SQL映射文件UserinfoMapper.xml中的代码都是错误的,此时要删除client、entity和sqlmapping包以及包中的所有文件,因为要重新进行逆向。
为了在insert语句中使用Oracle数据库中的序列来对id列进行传值,在逆向之前需要更改generatorConfig.xml配置文件中的配置,添加<generatedKey>标签后的配置代码如下:
<table schema="y2" tableName="userinfo">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
标签<generatedKey>的主要作用就是在生成insert的SQL语句时使用名称为idauto序列的nextval值来作为主键的id值,identity="false"属性说明主键不是自增的,而是由序列生成的。
如果想同时逆向多个表,那么在generatorConfig.xml文件中写入多个<table>标签即可,示例代码如下:
<table schema="y2" tableName="userinfo">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
<table schema="y2" tableName="A">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
<table schema="y2" tableName="B">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
配置文件generatorConfig.xml准备结束后重新进行逆向操作,在sqlmapping包中生成最新版正确的SQL映射文件。最新版正确的insert语句如下:
<insert id="insert" parameterType="entity.Userinfo">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Mon Sep 10 17:11:11 CST 2018.
-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long">
select idauto.nextval from dual
</selectKey>
insert into Y2.USERINFO (ID, USERNAME, PASSWORD
)
values (#{id,jdbcType=NUMERIC}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}
)
</insert>
再次运行Test.java类,成功在数据表中添加了1条记录。
本节就要使用MyBatis Generator工具逆向的代码操作MySQL数据库。
MySQL在默认情况下是不允许远程连接到数据库的,连接时会出现异常信息:
"Host '某个IP' is not allowed to connect to this MySQL server"
因此,要进行一下配置,步骤如下。
(1)在CMD中输入命令:mysql -uroot -p123,登录MySQL控制台。
(2)执行切换数据库命令:use mysql,切换到操作mysql数据库。
(3)执行更新SQL语句:update user set host = '%' where user = 'root';。
(4)重新启动MySQL服务后,远程就可以连接了。注意要关闭操作系统的防火墙。
MySQL数据库中的userinfo数据表结构如图1-37所示。
图1-37 数据表userinfo结构
创建名称为GeneratorMySQL的项目,更改配置文件generatorConfig.xml,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:mysql://localhost:3306/y2"
driverClass="com.mysql.jdbc.Driver" password="123123" userId="root" />
<javaModelGenerator targetPackage="entity"
targetProject="GeneratorMySQL" />
<sqlMapGenerator targetPackage="sqlmapping"
targetProject="GeneratorMySQL" />
<javaClientGenerator targetPackage="client"
targetProject="GeneratorMySQL" type="XMLMAPPER" />
<table schema="y2" tableName="userinfo">
</table>
</context>
</generatorConfiguration>
注意:
在连接MySQL数据库时,<table>标记的schema属性值y2是操作MySQL目标数据库的名字。
对generatorConfig.xml文件进行逆向时,会出现错误,效果如图1-38所示。
图1-38 缺少MySQL的JDBC驱动程序
异常信息提示找不到MySQL的JDBC驱动,解决异常的办法是在MyBatisJAR库中添加MySQL的JDBC驱动,效果如图1-39所示。
图1-39 在MyBatisJAR库中添加MySQL驱动
MySQL的JDBC驱动JAR包添加成功后再次执行逆向操作,又出现异常效果,如图1-40所示。
图1-40 设置时区有异常
解决时区的问题需要进入文件夹C:\ProgramData\MySQL\MySQL Server 8.0并编辑my.ini文件,在[mysqld]节点下添加配置代码:
[mysqld]
default-time-zone='+08:00'
重启MySQL服务后再次进行逆向没有出现异常,表明成功逆向,Java项目结构如图1-41所示。
图1-41 成功逆向后的项目结构
在src中创建mybatis-config.xml文件,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/y2" />
<property name="username" value="root" />
<property name="password" value="123123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/UserinfoMapper.xml" />
</mappers>
</configuration>
运行类代码如下:
public class Test {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(123);
userinfo.setInsertdate(new Date());
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
SqlSession session = factory.openSession();
session.insert("insert", userinfo);
session.commit();
session.close();
}
}
程序运行后在userinfo数据表中添加了新的记录。
前面章节都是使用MyBatis Generator插件生成的实体类和SQL映射文件来操作数据库,并不能从基础上掌握MyBatis框架的使用,本小节将从零起步,以自搭建开发环境开始,再到使用SqlSession对象实现经典功能CURD,并且是针对Oracle和MySQL这两种主流数据库的。
本节将演示使用SqlSession对Oracle数据库进行CURD操作。
(1)创建数据表。创建userinfo数据表,表结构如图1-42所示。
图1-42 userinfo数据表结构
(2)创建名称为mybatis_sqlsession_curd_oracle的Java项目。
(3)准备generatorConfig.xml逆向配置文件,主要作用是根据数据表结构只逆向出实体类,不包含SQL映射文件以及客户端代码,配置代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
driverClass="oracle.jdbc.OracleDriver" password="123" userId="y2" />
<javaModelGenerator targetPackage="entity"
targetProject="mybatis_sqlsession_curd_oracle" />
<table schema="y2" tableName="userinfo">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
</context>
</generatorConfiguration>
开始进行逆向操作,逆向出实体类。
(4)在src路径下创建连接数据库的配置文件mybatis-config.xml,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url"
value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="username" value="y2" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/userinfoMapping.xml" />
</mappers>
</configuration>
sqlmapping包中的userinfoMapping.xml是SQL映射文件。因为本节是从零起步开始搭建开发环境,所以与 SQL 映射有关的配置代码需要自己手写,这也是本节的重点,sqlmapping文件并不通过MyBatis Generator插件获得。
(5)SQL映射文件userinfoMapping.xml的初始内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="mybatis.testcurd">
</mapper>
(6)创建获取SqlSession对象的工具类,代码如下:
public class DBTools {
public static SqlSession getSqlSession() throws IOException {
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
return factory.openSession();
}
}
(7)在项目中添加两个dtd文件,分别是src路径中的mybatis-3-config.dtd、sqlmapping包中的mybatis-3-mapper.dtd。添加这两个dtd文件的目的是在开发XML配置文件或SQL映射文件时实现自动提示功能。这两个dtd文件复制自mybatis.jar文件中的org\apache\ibatis\builder\xml路径下。
在SQL映射文件userinfoMapping.xml中添加如下配置代码:
<insert id="insertUserinfo" parameterType="entity.Userinfo">
<selectKey resultType="java.lang.Long" keyProperty="id"
order="BEFORE">
select idauto.nextval from dual
</selectKey>
insert into
userinfo(id,username,password,age,insertDate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</insert>
其中<selectKey>的order="BEFORE"属性的含义是select语句比insert语句先执行,resultType的属性值java.lang.Long表示将序列返回的数字转成Long类型,keyProperty="id"的作用是将这个Long值放入parameterType的Userinfo的id属性中。
<selectKey resultType="java.lang.Long" keyProperty="id"
order="BEFORE">
select idauto.nextval from dual
</selectKey>
此段配置代码的主要功能是根据序列对象生成一个主键id值,将id值放入Userinfo对象的id属性中,然后再执行insert语句插入到数据表里。使用序列生成的id值还可以在代码中获取,也就是插入一条记录后使用程序代码可以从Userinfo对象的id属性中获取刚才插入记录的id值。
属性parameterType定义参数类型,属性resultType定义返回值的类型。
创建Java类,核心代码如下:
public class Insert1 {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(100L);
userinfo.setInsertdate(new Date());
SqlSession session = DBTools.getSqlSession();
session.insert("insertUserinfo", userinfo);
session.commit();
session.close();
System.out.println(userinfo.getId());
}
}
方法insert() 的第1个参数是SQL映射文件配置<insert id="insertUserinfo">的id值,代表要执行哪个SQL语句。
程序执行后在控制台输出刚才新添加记录的id主键值,并且在userinfo数据表中可以看到新的记录。
如果方法insert() 的第1个参数值在SQL映射文件中并不存在,则运行程序出现异常,创建测试,代码如下:
public class Insert2 {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(100L);
userinfo.setInsertdate(new Date());
SqlSession session = DBTools.getSqlSession();
session.insert("insert2", userinfo);
session.commit();
session.close();
System.out.println(userinfo.getId());
}
}
程序运行后出现异常:
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for insert2
异常信息提示映射集合中不存在insert2这个映射名。
在SQL映射文件userinfoMapping.xml中添加如下配置代码:
<select id="getUserinfoById" parameterType="long"
resultType="entity.Userinfo">
select * from
userinfo where id=#{id}
</select>
创建Java类,核心代码如下:
public class SelectById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("getUserinfoById", 600410L);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
程序运行后在控制台输出单条记录的信息。
在SQL映射文件userinfoMapping.xml中添加如下配置代码:
<select id="getAllUserinfo" resultType="entity.Userinfo">
select * from userinfo
</select>
创建Java类,核心代码如下:
public class SelectAll {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("getAllUserinfo");
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
程序运行后在控制台输出全部记录的信息。
在SQL映射文件userinfoMapping.xml中添加如下配置代码:
<update id="updateUserinfoById" parameterType="entity.Userinfo">
update userinfo
set
username=#{username},password=#{password},age=#{age},insertDate=#{insertdate}
where id=#{id}
</update>
创建Java类,核心代码如下:
public class UpdateById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("getUserinfoById", 600410L);
userinfo.setUsername("x");
userinfo.setPassword("xx");
userinfo.setAge(200L);
session.update("updateUserinfoById", userinfo);
session.commit();
session.close();
}
}
程序运行后成功执行更新操作。
在SQL映射文件userinfoMapping.xml中添加如下配置代码:
<delete id="deleteUserinfoById" parameterType="long">
delete from
userinfo where id=#{id}
</delete>
创建Java类,核心代码如下:
public class DeleteById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
session.delete("deleteUserinfoById", 600410L);
session.commit();
session.close();
}
}
程序运行后成功执行删除操作。
到此,自搭建开发环境并使用SqlSession针对Oracle数据库的CURD操作结束。
本节将演示使用SqlSession对MySQL数据库进行CURD操作。
(1)使用原有的userinfo数据表。
(2)创建名称为mybatis_sqlsession_curd_mysql的Java项目。
(3)准备generatorConfig.xml逆向配置文件,主要作用是根据数据表结构只逆向出实体类,不包含SQL映射文件以及客户端代码,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:mysql://localhost:3306/y2"
driverClass="com.mysql.jdbc.Driver" password="123123" userId="root" />
<javaModelGenerator targetPackage="entity"
targetProject="mybatis_sqlsession_curd_mysql" />
<table schema="y2" tableName="userinfo">
</table>
</context>
</generatorConfiguration>
开始进行逆向操作,逆向出实体类。
(4)在src路径下创建连接数据库的配置文件mybatis-config.xml,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/y2" />
<property name="username" value="root" />
<property name="password" value="123123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/userinfoMapping.xml" />
</mappers>
</configuration>
sqlmapping包中的userinfoMapping.xml是SQL映射文件。因为本节是从零起步开始搭建开发环境,所以与SQL映射有关的配置代码需要自己手写,并不能通过MyBatis Generator插件获得。
(5)SQL映射文件userinfoMapping.xml的代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="mybatis.testcurd">
<insert id="insertUserinfo" parameterType="entity.Userinfo"
useGeneratedKeys="true" keyProperty="id">
insert into
userinfo(username,password,age,insertdate)
values(#{username},#{password},#{age},#{insertdate})
</insert>
<select id="getUserinfoById" parameterType="long"
resultType="entity.Userinfo">
select *
from userinfo where id=#{userId}
</select>
<select id="getAllUserinfo" resultType="entity.Userinfo">
select * from userinfo
order
by id asc
</select>
<delete id="deleteUserinfoById" parameterType="long">
delete from
userinfo where
id=#{userId}
</delete>
<update id="updateUserinfoById" parameterType="entity.Userinfo">
update userinfo
set
username=#{username},
password=#{password},
age=#{age},
insertdate=#{insertdate}
where id=#{id}
</update>
</mapper>
下列配置代码中的useGeneratedKeys="true"代表使用数据库的主键自增机制:
<insert id="insertUserinfo" parameterType="entity.Userinfo"
useGeneratedKeys="true" keyProperty="id">
insert into
userinfo(username,password,age,insertdate)
values(#{username},#{password},#{age},#{insertdate})
</insert>
由于MySQL数据库的主键具有自增机制,因此在这里不使用类似于Oracle数据库的序列来产生主键值。属性keyProperty="id"代表把最新的id值放入entity.Userinfo的id属性里,以方便在程序代码中获得刚刚插入的记录id值。
(6)创建获取SqlSession对象的工具类,代码如下:
public class DBTools {
public static SqlSession getSqlSession() throws IOException {
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
return factory.openSession();
}
}
(7)在项目中添加两个dtd文件,分别是src路径中的mybatis-3-config.dtd、sqlmapping包中的mybatis-3-mapper.dtd,添加这两个dtd文件是为了在开发XML配置文件或SQL映射文件时实现自动提示功能,这两个dtd文件复制自mybatis.jar文件中的org\apache\ibatis\builder\xml路径下。
创建Java类,代码如下:
public class Insert1 {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(100);
userinfo.setInsertdate(new Date());
SqlSession session = DBTools.getSqlSession();
session.insert("insertUserinfo", userinfo);
session.commit();
session.close();
System.out.println(userinfo.getId());
}
}
核心代码和操作Oracle数据库的代码基本一致,运行后在控制台输出刚刚插入记录的id主键值,并且在MySQL数据表中添加新的记录。
其他业务方法的代码和操作Oracle数据库大体一致,并且已经成功运行,详细程序可查阅随书下载的源代码。
到此,自搭建开发环境并使用SqlSession针对MySQL数据库的CURD操作到结束。
常见的向SQL映射传入参数类型有如下5种。
(1)传入简单数据类型。
(2)传入复杂数据类型。
(3)传入Map数据类型。
(4)传入简单数组/复杂数组数据类型。
(5)传入List<Long/Entity/Map>数据类型。
创建新的项目mybatis_sqlsession_parameterType来测试这5种情况。
先来测试第1种:传入简单数据类型。
SQL映射代码如下:
<select id="test1" parameterType="long"
resultType="entity.Userinfo">
select * from userinfo
where id=#{id}
</select>
创建Java类,代码如下:
public class Test1 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("test1", 100L);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第2种:传入复杂数据类型。
SQL映射代码如下:
<select id="test2" parameterType="entity.Userinfo"
resultType="entity.Userinfo">
select * from userinfo
where id=#{id}
</select>
创建Java类,代码如下:
public class Test2 {
public static void main(String[] args) throws IOException {
Userinfo queryUserinfo = new Userinfo();
queryUserinfo.setId(100L);
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("test2", queryUserinfo);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第3种:传入Map数据类型。
SQL映射代码如下:
<select id="test3" parameterType="map"
resultType="entity.Userinfo">
select * from userinfo
where id=#{id}
</select>
创建Java类,代码如下:
public class Test3 {
public static void main(String[] args) throws IOException {
Map map = new HashMap();
map.put("id", 100);
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("test3", map);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第4种:传入简单数组/复杂数组数据类型。
先来看一下以简单数据类型的数组作为参数进行传递的情况。
SQL映射代码如下:
<select id="test41" parameterType="int[]"
resultType="entity.Userinfo">
select * from userinfo
where id=#{array[0]} or
id=#{array[1]} or
id=#{array[2]}
</select>
创建Java类,代码如下:
public class Test41 {
public static void main(String[] args) throws IOException {
long[] idArray = new long[] { 88, 89, 90 };
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test41", idArray);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
再来看一下以复杂数据类型的数组作为参数进行传递的情况。
SQL映射代码如下:
<select id="test42" parameterType="Object[]"
resultType="entity.Userinfo">
select * from userinfo
where id=#{array[0].id} or
id=#{array[1].id} or
id=#{array[2].id}
</select>
创建Java类,代码如下:
public class Test42 {
public static void main(String[] args) throws IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setId(88L);
Userinfo userinfo2 = new Userinfo();
userinfo2.setId(89L);
Userinfo userinfo3 = new Userinfo();
userinfo3.setId(90L);
Userinfo[] userinfoArray = new Userinfo[3];
userinfoArray[0] = userinfo1;
userinfoArray[1] = userinfo2;
userinfoArray[2] = userinfo3;
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test42", userinfoArray);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
最后测试第5种:传入List<Long/Entity/Map> 数据类型。
(1)List中存储简单数据类型的SQL映射,代码如下:
<select id="test51" parameterType="list"
resultType="entity.Userinfo">
select * from userinfo
where id=#{list[0]} or
id=#{list[1]}
or
id=#{list[2]}
</select>
创建Java类,代码如下:
public class Test51 {
public static void main(String[] args) throws IOException {
List idList = new ArrayList();
idList.add(88);
idList.add(89);
idList.add(90);
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test51", idList);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
(2)List中存储复杂数据类型的SQL映射,代码如下:
<select id="test52" parameterType="list"
resultType="entity.Userinfo">
select * from userinfo
where id=#{list[0].id} or
id=#{list[1].id}
</select>
创建Java类,代码如下:
public class Test52 {
public static void main(String[] args) throws IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setId(88L);
Userinfo userinfo2 = new Userinfo();
userinfo2.setId(89L);
List idList = new ArrayList();
idList.add(userinfo1);
idList.add(userinfo2);
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test52", idList);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
(3)List中存储Map数据类型的SQL映射,代码如下:
<select id="test53" parameterType="list"
resultType="entity.Userinfo">
select * from userinfo
where id=#{list[0].myKey1} or
id=#{list[1].myKey2} or
id=#{list[2].myKey3}
</select>
创建Java类,代码如下:
public class Test53 {
public static void main(String[] args) throws IOException {
Map map1 = new HashMap();
map1.put("myKey1", 88L);
Map map2 = new HashMap();
map2.put("myKey2", 89L);
Map map3 = new HashMap();
map3.put("myKey3", 90L);
List list = new ArrayList();
list.add(map1);
list.add(map2);
list.add(map3);
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test53", list);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
常见的从SQL映射取得返回值类型有如下3种类型。
(1)返回简单数据类型。
(2)返回复杂数据类型。
(3)返回Map数据类型。
在项目mybatis_sqlsession_resultType中测试这3种情况。
先来测试第1种:返回简单数据类型。
SQL映射代码如下:
<select id="test1" resultType="int">
select count(*) from
userinfo
</select>
创建Java类,代码如下:
public class Test1 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
int count = session.selectOne("test1");
System.out.println("count=" + count);
session.commit();
session.close();
}
}
继续测试第2种:返回复杂数据类型。
SQL映射代码如下:
<select id="test2" resultType="entity.Userinfo">
select * from userinfo
where id=100
</select>
创建Java类,代码如下:
public class Test2 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Userinfo userinfo = session.selectOne("test2");
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第3种:返回Map数据类型。
SQL映射代码如下:
<select id="test31" resultType="map">
select * from userinfo
where
id=90
</select>
创建Java类,代码如下:
public class Test31 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Map map = session.selectOne("test31");
System.out.println(map.get("ID"));
System.out.println(map.get("USERNAME"));
System.out.println(map.get("PASSWORD"));
System.out.println(map.get("AGE"));
System.out.println(map.get("INSERTDATE"));
session.commit();
session.close();
}
}
从map中取值时,传入的key必须是大写的形式,因为本实验操作的数据库是Oracle,该数据库所有的列名都是大写的形式。
如果查询的结果是动态计算出来的,那么可以使用以下两种方式来获得列中的值。
(1)使用别名。
SQL映射代码如下:
<select id="test32" resultType="map">
select sum(age) "sumAge" from
userinfo
</select>
创建Java类,代码如下:
public class Test32 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Map map = session.selectOne("test32");
System.out.println(map.get("sumAge"));
session.commit();
session.close();
}
}
(2)使用默认列名。
SQL映射代码如下:
<select id="test33" resultType="map">
select sum(age) from userinfo
</select>
创建Java类,代码如下:
public class Test33 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
Map map = session.selectOne("test33");
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
System.out.println("列名:" + iterator.next());
}
System.out.println(map.get("SUM(AGE)"));
session.commit();
session.close();
}
}
selectList()方法返回的List中可以存储简单、复杂以及Map数据类型。
(1)List中存储简单数据类型。
如果resultType属性值是long,SQL映射示例代码如下:
<select id="test6" resultType="long">
select id from userinfo
order by id
asc
</select>
selectList()方法返回的数据类型是List<Long>,运行类代码如下:
public class Test6 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
List<Long> listMap = session.selectList("test6");
for (int i = 0; i < listMap.size(); i++) {
System.out.println(listMap.get(i));
}
session.commit();
session.close();
}
}
(2)List中存储复杂数据类型。
如果执行如下的SQL映射代码:
<select id="test4" resultType="entity.Userinfo">
select * from userinfo
order by id
asc
</select>
后查询的结果大于1条记录,则selectList()方法返回的数据类型是List<Userinfo>,因为resultType属性值是entity.Userinfo实体类数据类型,运行类代码如下:
public class Test4 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
List<Userinfo> listUserinfo = session.selectList("test4");
System.out.println(listUserinfo.size());
session.commit();
session.close();
}
}
(3)List中存储Map。
同理,如果resultType属性值是map,SQL映射示例代码如下:
<select id="test5" resultType="map">
select * from userinfo
order by id
asc
</select>
selectList()方法返回的数据类型是List<Map>,运行类代码如下:
public class Test5 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
List<Map> listMap = session.selectList("test5");
System.out.println(listMap.size());
session.commit();
session.close();
}
}
当多个SQL映射文件中的id值一样时,使用SqlSession操作数据库时会出现异常。
创建测试用的namespaceError项目,并创建userinfoMappingA.xml映射文件,代码如下:
<mapper namespace="AAAAA">
<insert id="insertUserinfo" parameterType="entity.Userinfo">
insert into
userinfo(id,username)
values(idauto.nextval,'A')
</insert>
</mapper>
创建名称为userinfoMappingB.xml的映射文件,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BBBBB">
<insert id="insertUserinfo" parameterType="entity.Userinfo">
insert into
userinfo(id,username)
values(idauto.nextval,'B')
</insert>
</mapper>
再将这两个SQL映射文件使用<mapper>标签注册到mybatis-config.xml配置文件中,代码如下:
<mappers>
<mapper resource="mapping/userinfoMappingA.xml" />
<mapper resource="mapping/userinfoMappingB.xml" />
</mappers>
在这两个SQL映射文件中都有<insert>的id属性值为insertUserinfo的配置代码,那么在执行如下Java代码后就出现了异常:
public class Insert1 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
session.insert("insertUserinfo");
session.commit();
session.close();
}
}
异常信息如下:
Caused by: java.lang.IllegalArgumentException: insertUserinfo is ambiguous in Mapped Statements collection (try using the full name including the namespace, or rename one of the entries)
异常信息提示insertUserinfo并不是确定的名字,而是模糊的。这时可以尝试使用全名称的方式来调用此SQL映射。所谓的“全名称”方式就是指sqlId之前写上命名空间,对命名空间的命名可以写上表的名称,或者是业务的名称,这样有助于区分重复的sqlId,更改后的Java代码如下:
public class Insert2 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
session.insert("BBBBB.insertUserinfo");
session.commit();
session.close();
}
}
执行insert()方法时在第1个参数加上了命名空间BBBBB,使用.小数点作为间隔,再执行程序就不会出现异常了。
命名空间的主要作用是防止SQL映射中的id值一样导致出现异常。
本节将从零起步,以自搭建开发环境开始,再到使用Mapper接口实现经典功能CURD,并且针对Oracle和MySQL这两种主流数据库。
前面知识点中的代码都是使用如下程序对数据库进行操作:
sqlSession.insert("sqlId");
sqlSession.delete("sqlId");
sqlSession.update("sqlId");
sqlSession.selectList("sqlId");
对方法insert()、delete()、update()和selectList()传入String sqlId来达到调用SQL语句进而对数据库操作的目的,完全是面向String字符串类型的sqlId编程。虽然能达到实现操作数据库的目的,但这种代码的写法却是不规范的。理想中规范的写法不是面向String sqlId编程,而是面向接口编程。新版的MyBatis提供了“接口-SQL映射”功能,使程序员完全面向Mapper接口进行编程,相比String sqlId的方式,使用Mapper在代码规范方面上了一个台阶。
“接口-SQL映射”的对应关系如图1-43所示。
图1-43 接口-SQL映射原理
“接口-SQL映射”的原理如下。
SQL映射文件UserinfoMapper.xml中的namespace属性值sqlmapping.UserinfoMapper代表该映射对应的就是sqlmapping包中的UserinfoMapper.java接口,而<insert>标签的id属性值insertUserinfo就是UserinfoMapper.java接口中的public void insertUserinfo(Userinfo userinfo)方法,<insert>标签的parameterType属性值sqlmapping.Userinfo就是public void insertUserinfo (Userinfo userinfo)方法参数类型,只要它们一一对应,就能实现“接口-SQL映射”,程序员完全以面向接口的方式设计软件。
本节将演示使用Mapper接口对Oracle数据库进行CURD操作。
(1)使用原有userinfo数据表。
(2)创建名称为mybatis_mapper_curd_oracle的Java项目。
(3)准备generatorConfig.xml逆向配置文件,主要作用是根据数据表结构只逆向出实体类,不包含SQL映射文件以及客户端代码,配置代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
driverClass="oracle.jdbc.OracleDriver" password="123" userId="y2" />
<javaModelGenerator targetPackage="entity"
targetProject="mybatis_mapper_curd_oracle" />
<table schema="y2" tableName="userinfo">
<generatedKey column="id"
sqlStatement="select idauto.nextval from dual" identity="false" />
</table>
</context>
</generatorConfiguration>
开始进行逆向操作,逆向出实体类。
(4)在src路径下创建连接数据库的配置文件mybatis-config.xml,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url"
value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="username" value="y2" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/UserinfoMapper.xml" />
</mappers>
</configuration>
sqlmapping包中的UserinfoMapper.xml是SQL映射文件。因为本节是从零起步搭建开发环境,所以与SQL映射有关的配置代码需要自己手写,并不能通过MyBatis Generator插件获得。
(5)SQL映射文件UserinfoMapper.xml的初始代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="sqlmapping.UserinfoMapper">
</mapper>
SQL映射文件UserinfoMapper.xml的名称可以和接口名称不一样,比如SQL映射文件名称为UserinfoMapper.xml,接口名称是XXUserinfoMapper.java,可以在SQL映射文件中配置代码如下:
<mapper namespace="sqlmapping.XXUserinfoMapper">
这样的写法也是正确的。
(6)创建与UserinfoMapper.xml文件匹配的Mapper映射接口UserinfoMapper.java,接口UserinfoMapper.java的初始代码如下:
public interface UserinfoMapper {
}
(7)创建获取SqlSession对象的工具类,代码如下:
public class DBTools {
public static SqlSession getSqlSession() throws IOException {
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
return factory.openSession();
}
}
(8)在项目中添加两个dtd文件,分别是src路径中的mybatis-3-config.dtd、sqlmapping包中的mybatis-3-mapper.dtd,添加这两个dtd文件是为了在开发XML配置文件或SQL映射文件时实现自动提示功能。这两个dtd文件复制自mybatis.jar文件中的org\apache\ibatis\builder\xml。
在SQL映射文件UserinfoMapper.xml中添加如下配置代码:
<insert id="insertUserinfo" parameterType="entity.Userinfo">
<selectKey order="BEFORE" resultType="java.lang.Long"
keyProperty="id">
select idauto.nextval from dual
</selectKey>
insert into userinfo(id,username,password,age,insertdate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</insert>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public void insertUserinfo(Userinfo userinfo);
}
创建Java类,核心代码如下:
public class Insert {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(100L);
userinfo.setInsertdate(new Date());
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
mapper.insertUserinfo(userinfo);
session.commit();
session.close();
System.out.println("id=" + userinfo.getId());
}
}
程序执行后在控制台输出刚才新添加记录的id主键值,并且在userinfo数据表中可以看到新的记录。
在SQL映射文件UserinfoMapper.xml中添加如下配置代码:
<select id="selectById" parameterType="long"
resultType="entity.Userinfo">
select *
from userinfo where id=#{userId}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo selectById(long userId);
}
创建Java类,核心代码如下:
public class SelectById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.selectById(600413L);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
程序运行后在控制台输出单条记录的信息。
在SQL映射文件UserinfoMapper.xml中添加如下配置代码:
<select id="selectAll" resultType="entity.Userinfo">
select * from userinfo order
by id asc
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> selectAll();
}
创建Java类,核心代码如下:
public class SelectAll {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.selectAll();
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
程序运行后在控制台输出全部记录的信息。
在SQL映射文件UserinfoMapper.xml中添加如下配置代码:
<update id="updateById" parameterType="entity.Userinfo">
update userinfo set
username=#{username},
password=#{password},
age=#{age},
insertdate=#{insertdate}
where id=#{id}
</update>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo selectById(long userId);
public void updateById(Userinfo userinfo);
}
创建Java类,核心代码如下:
public class UpdateById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.selectById(2142502L);
userinfo.setUsername("xxxxxxxxxxxx");
mapper.updateById(userinfo);
session.commit();
session.close();
}
}
程序运行后成功执行更新操作。
在SQL映射文件UserinfoMapper.xml中添加如下配置代码:
<delete id="deleteById" parameterType="long">
delete
from userinfo where
id=#{userId}
</delete>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public void deleteById(long userId);
}
创建Java类,核心代码如下:
public class DeleteById {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
mapper.deleteById(2152493L);
session.commit();
session.close();
}
}
程序运行后成功执行删除操作。
到此,自搭建开发环境并使用Mapper针对Oracle数据库的CURD操作结束。
本节将演示使用Mapper对MySQL数据库进行CURD操作。
(1)使用原有userinfo数据表。
(2)创建名称为mybatis_mapper_curd_mysql的Java项目。
(3)准备generatorConfig.xml逆向配置文件,主要作用是根据数据表结构只逆向出实体类,不包含SQL映射文件以及客户端代码,配置代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="context1">
<jdbcConnection
connectionURL="jdbc:mysql://localhost:3306/y2"
driverClass="com.mysql.jdbc.Driver" password="123123" userId="root" />
<javaModelGenerator targetPackage="entity"
targetProject="mybatis_mapper_curd_mysql" />
<table schema="y2" tableName="userinfo">
</table>
</context>
</generatorConfiguration>
开始进行逆向操作,逆向出实体类。
(4)在src路径下创建连接数据库的配置文件mybatis-config.xml,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/y2" />
<property name="username" value="root" />
<property name="password" value="123123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmapping/UserinfoMapper.xml" />
</mappers>
</configuration>
sqlmapping包中的UserinfoMapper.xml是SQL映射文件。因为本节是从零起步搭建开发环境,所以与SQL映射有关的配置代码需要自己手写,并不能通过MyBatis Generator插件获得。
(5)SQL映射文件UserinfoMapper.xml的代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="sqlmapping.UserinfoMapper">
<insert id="insertUserinfo" parameterType="entity.Userinfo"
useGeneratedKeys="true" keyProperty="id">
insert into
userinfo(id,username,password,age,insertdate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</insert>
<select id="selectAll" resultType="entity.Userinfo">
select * from userinfo order
by id asc
</select>
<select id="selectById" parameterType="long"
resultType="entity.Userinfo">
select *
from userinfo where id=#{userId}
</select>
<delete id="deleteById" parameterType="long">
delete
from userinfo where
id=#{userId}
</delete>
<update id="updateById" parameterType="entity.Userinfo">
update userinfo set
username=#{username},
password=#{password},
age=#{age},
insertdate=#{insertdate}
where id=#{id}
</update>
</mapper>
(6)创建与UserinfoMapper.xml文件匹配的Mapper映射接口UserinfoMapper.java,接口UserinfoMapper的代码如下:
public interface UserinfoMapper {
public void insertUserinfo(Userinfo userinfo);
public List<Userinfo> selectAll();
public Userinfo selectById(long userId);
public void deleteById(long userId);
public void updateById(Userinfo userinfo);
}
(7)创建获取SqlSession对象的工具类,代码如下:
public class DBTools {
public static SqlSession getSqlSession() throws IOException {
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configStream);
return factory.openSession();
}
}
(8)在项目中添加两个dtd文件,分别是src路径中的mybatis-3-config.dtd、sqlmapping包中的mybatis-3-mapper.dtd,添加这两个dtd文件是为了在开发XML配置文件或SQL映射文件时实现自动提示功能。这两个dtd文件复制自mybatis.jar文件中的org\apache\ibatis\builder\xml。
创建Java类,代码如下:
public class Insert {
public static void main(String[] args) throws IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword("中国人");
userinfo.setAge(100);
userinfo.setInsertdate(new Date());
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
mapper.insertUserinfo(userinfo);
session.commit();
session.close();
System.out.println("id=" + userinfo.getId());
}
}
核心代码和操作Oracle数据库的代码基本一致,运行后在控制台输出刚刚插入记录的id主键值,并且在MySQL数据表中添加了新的记录。
其他业务方法的代码和操作Oracle数据库大体一致,并且已经成功运行,详细程序可查阅随书下载的源代码。
到此,自搭建开发环境并使用Mapper针对MySQL数据库的CURD操作结束。
SQL映射代码如下:
常见的向Mapper接口传入参数类型有如下5种。
(1)传入简单数据类型。
(2)传入复杂数据类型。
(3)传入Map数据类型。
(4)传入简单数组/复杂数组数据类型。
(5)传入List<Long/Entity/Map>数据类型。
创建新的项目mybatis_mapper_parameterType来测试这5种情况。
先来测试第1种:传入简单数据类型。
参数个数为1个的SQL映射代码如下:
<select id="test1" resultType="entity.Userinfo">
select *
from userinfo where
id=#{id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo test1(long id);
}
创建Java类,代码如下:
public class Test1 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.test1(89L);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
参数个数为多个的SQL映射代码如下:
<select id="test11" resultType="entity.Userinfo">
select *
from userinfo where
id=#{arg0} or id=#{arg1}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test11(long id1, long id2);
}
创建Java类,代码如下:
public class Test11 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test11(88L, 89L);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
继续测试第2种:传入复杂数据类型。
参数个数为1个的SQL映射代码如下:
<select id="test2" resultType="entity.Userinfo">
select *
from userinfo where
id=#{id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo test2(Userinfo userinfo);
}
创建Java类,代码如下:
public class Test2 {
public static void main(String[] args) throws IOException {
Userinfo queryUserinfo = new Userinfo();
queryUserinfo.setId(89L);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.test2(queryUserinfo);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
参数个数为多个的SQL映射代码如下:
<select id="test22" resultType="entity.Userinfo">
select *
from userinfo where
id=#{arg0.id} or id=#{arg1.id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test22(Userinfo userinfo1, Userinfo userinfo2);
}
创建Java类,代码如下:
public class Test22 {
public static void main(String[] args) throws IOException {
Userinfo queryUserinfo1 = new Userinfo();
queryUserinfo1.setId(89L);
Userinfo queryUserinfo2 = new Userinfo();
queryUserinfo2.setId(90L);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test22(queryUserinfo1, queryUserinfo2);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
继续测试第3种:传入Map数据类型。
SQL映射代码如下:
<select id="test3" resultType="entity.Userinfo">
select *
from userinfo where
id=#{findId}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo test3(Map map);
}
创建Java类,代码如下:
public class Test3 {
public static void main(String[] args) throws IOException {
Map map = new HashMap();
map.put("findId", 89L);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.test3(map);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第4种:传入简单数组/复杂数组数据类型。
先来看一下简单数组数据类型作为参数进行传递的情况。
<select id="test41" resultType="entity.Userinfo">
select *
from userinfo where
id=#{array[0]} or
id=#{array[1]} or
id=#{array[2]}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test41(long[] idArray);
}
创建Java类,代码如下:
public class Test41 {
public static void main(String[] args) throws IOException {
long[] idArray = new long[] { 88, 89, 90 };
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test41(idArray);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
再来看一下复杂数组数据类型作为参数进行传递的情况。
SQL映射代码如下:
<select id="test42" resultType="entity.Userinfo">
select *
from userinfo where
id=#{array[0].id} or
id=#{array[1].id} or
id=#{array[2].id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test42(Userinfo[] userinfoArray);
}
创建Java类,代码如下:
public class Test42 {
public static void main(String[] args) throws IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setId(88L);
Userinfo userinfo2 = new Userinfo();
userinfo2.setId(89L);
Userinfo userinfo3 = new Userinfo();
userinfo3.setId(90L);
Userinfo[] userinfoArray = { userinfo1, userinfo2, userinfo3 };
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test42(userinfoArray);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
最后来看一下简单数组与复杂数据联合使用作为参数进行传递的情况。
SQL映射代码如下:
<select id="test43" resultType="entity.Userinfo">
select *
from userinfo where
id=#{arg0[0]} or
id=#{arg0[1]} or
id=#{arg1[0].id} or id=#{arg1[1].id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test43(long[] idArray, Userinfo[] userinfoArray);
}
创建Java类,代码如下:
public class Test43 {
public static void main(String[] args) throws IOException {
long[] idArray = new long[] { 88, 89 };
Userinfo userinfo1 = new Userinfo();
userinfo1.setId(90L);
Userinfo userinfo2 = new Userinfo();
userinfo2.setId(91L);
Userinfo[] userinfoArray = { userinfo1, userinfo2 };
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test43(idArray, userinfoArray);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
最后测试第5种:传入List<Long/Entity/Map>数据类型。
(1)List中存储简单数据类型的SQL映射代码如下:
<select id="test51" resultType="entity.Userinfo">
select *
from userinfo where
id=#{list[0]} or
id=#{list[1]}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test51(List<Long> idList);
}
创建Java类,代码如下:
public class Test51 {
public static void main(String[] args) throws IOException {
List idList = new ArrayList();
idList.add(88);
idList.add(89);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test51(idList);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
(2)List中存储复杂数据类型的SQL映射代码如下:
<select id="test52" resultType="entity.Userinfo">
select *
from userinfo where
id=#{list[0].id} or
id=#{list[1].id}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test52(List<Userinfo> idList);
}
创建Java类,代码如下:
public class Test52 {
public static void main(String[] args) throws IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setId(88L);
Userinfo userinfo2 = new Userinfo();
userinfo2.setId(89L);
List listParam = new ArrayList();
listParam.add(userinfo1);
listParam.add(userinfo2);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test52(listParam);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
(3)List中存储Map数据类型的SQL映射代码如下:
<select id="test53" resultType="entity.Userinfo">
select *
from userinfo where
id=#{list[0].myKey1} or
id=#{list[1].myKey2}
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test53(List<Map> idList);
}
创建Java类,代码如下:
public class Test53 {
public static void main(String[] args) throws IOException {
Map map1 = new HashMap();
map1.put("myKey1", 89L);
Map map2 = new HashMap();
map2.put("myKey2", 90L);
List listParam = new ArrayList();
listParam.add(map1);
listParam.add(map2);
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test53(listParam);
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
在Mapper接口文件UserinfoMapper.java中添加如下代码:
常见的从SQL映射取得返回值类型有如下3种。
(1)返回简单数据类型。
(2)返回复杂数据类型。
(3)返回Map数据类型。
在项目mybatis_mapper_resultType中测试这3种情况。
先来测试第1种:返回简单数据类型。
SQL映射代码如下:
<select id="test1" resultType="int">
select count(*) from
userinfo
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public int test1();
}
创建Java类,代码如下:
public class Test1 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
int count = mapper.test1();
System.out.println("count=" + count);
session.commit();
session.close();
}
}
继续测试第2种:返回复杂数据类型。
SQL映射代码如下:
<select id="test2" resultType="entity.Userinfo">
select * from userinfo
where id=90
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Userinfo test2();
}
创建Java类,代码如下:
public class Test2 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Userinfo userinfo = mapper.test2();
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
session.commit();
session.close();
}
}
继续测试第3种:返回Map数据类型。
SQL映射代码如下:
<select id="test31" resultType="map">
select * from userinfo
where id=90
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Map test31();
}
创建Java类,代码如下:
public class Test31 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Map map = mapper.test31();
System.out.println(map.get("ID"));
System.out.println(map.get("USERNAME"));
System.out.println(map.get("PASSWORD"));
System.out.println(map.get("AGE"));
System.out.println(map.get("INSERTDATE"));
session.commit();
session.close();
}
}
如果查询的结果是动态计算出来的,那么可以使用以下两种方式来获得列中的值。
(1)使用别名。
SQL映射代码如下:
<select id="test32" resultType="map">
select sum(age) "sumAge" from
userinfo
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public Map test32();
}
创建Java类,代码如下:
public class Test32 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Map map = mapper.test32();
System.out.println(map.get("sumAge"));
session.commit();
session.close();
}
}
(2)使用默认列名。
SQL映射代码如下:
<select id="test33" resultType="map">
select sum(age) from userinfo
</select>
public interface UserinfoMapper {
public Map test33();
}
创建Java类,代码如下:
public class Test33 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
Map map = mapper.test33();
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
System.out.println("列名:" + iterator.next());
}
System.out.println(map.get("SUM(AGE)"));
session.commit();
session.close();
}
}
方法selectList()返回的List中可以存储简单、复杂和Map数据类型。
(1)List中存储简单数据类型。
SQL映射示例代码如下:
<select id="test6" resultType="long">
select id from userinfo
order by id
asc
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Long> test6();
}
运行类代码如下:
public class Test6 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Long> listLong = mapper.test6();
for (int i = 0; i < listLong.size(); i++) {
Long eachId = listLong.get(i);
System.out.println(eachId);
}
session.commit();
session.close();
}
}
(2)List中存储复杂数据类型。
SQL映射示例代码如下:
<select id="test4" resultType="entity.Userinfo">
select * from userinfo
order by id
asc
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Userinfo> test4();
}
运行类代码如下:
public class Test4 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Userinfo> listUserinfo = mapper.test4();
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System.out.println(userinfo.getId() + " " + userinfo.getUsername() + " " + userinfo.getPassword() + " "
+ userinfo.getAge() + " " + userinfo.getInsertdate());
}
session.commit();
session.close();
}
}
(3)List中存储Map数据类型。
SQL映射示例代码如下:
<select id="test5" resultType="map">
select * from userinfo
order by id
asc
</select>
在Mapper接口文件UserinfoMapper.java中添加如下代码:
public interface UserinfoMapper {
public List<Map> test5();
}
运行类代码如下:
public class Test5 {
public static void main(String[] args) throws IOException {
SqlSession session = DBTools.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
List<Map> listMap = mapper.test5();
for (int i = 0; i < listMap.size(); i++) {
Map map = listMap.get(i);
System.out.println(map.get("ID") + " " + map.get("USERNAME") + " " + map.get("PASSWORD") + " "
+ map.get("AGE") + " " + map.get("INSERTDATE"));
}
session.commit();
session.close();
}
}
在前面对两种主流数据库实现基本的CURD后,对MyBatis核心对象在使用上应该不再陌生,本节将会继续介绍这些核心对象的生命周期。
对象的生命周期也就是对象从创建到销毁的过程,但在此过程中,如果实现的代码质量不太优质,那么很容易造成程序上的错误或效率的降低。
(1)SqlSessionFactoryBuilder对象可以被 JVM 所实例化,使用或者销毁。一旦使用SqlSession FactoryBuilder对象创建了SqlSessionFactory后,SqlSessionFactoryBuilder类就不需要存在了,也就是不需要保持此对象的状态,可以任由JVM销毁,因此SqlSessionFactoryBuilder对象的最佳使用范围是在方法之内,也就是说,可以在方法内部声明SqlSessionFactoryBuilder对象来创建SqlSessionFactory对象。
(2)SqlSessionFactory对象是由SqlSessionFactoryBuilder对象创建而来。一旦SqlSessionFactory类的实例被创建,该实例在应用程序执行期间应该始终存在,根本不需要每一次操作数据库时都重新创建它。因为创建SqlSessionFactory对象比较耗时,所以应用它的最佳方式就是写一个单例模式,或使用Spring框架来实现单例模式以对SqlSessionFactory对象进行有效的管理。SqlSessionFactory对象是线程安全的。
(3)SqlSession对象是由SqlSessionFactory类创建而来,需要注意的是,每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,因为它是线程不安全的,所以千万不要在Servlet中声明该对象的1个实例变量。因为Servlet是单例的,声明成实例变量会造成线程安全问题,也绝不能将SqlSession对象放在一个类的静态字段甚至是实例字段中,还不可以将SqlSession对象放在HttpSession会话或ServletContext上下文中。在接收到HTTP请求后可以打开1个SqlSession对象操作数据库,在响应之前需要关闭SqlSession。关闭SqlSession很重要,读者应该确保使用finally块来关闭它。下面的示例就是一个确保SqlSession对象正常关闭的基本模式代码:
public class insertUserinfo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
SqlSession sqlSession = GetSqlSession.getSqlSession();
try {
// sqlSession curd code
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
根据前面学习到的生命周期的知识,后面将对MyBatis核心代码进行封装,这样更有助于提高数据CURD的方便性。
创建测试项目,名称为mybatis_threadlocal。
创建GetSqlSessionFactory类,完整代码如下:
public class GetSqlSessionFactory {
private static SqlSessionFactory factory;
private GetSqlSessionFactory() {
}
synchronized public static SqlSessionFactory getSqlSessionFactory() throws IOException {
if (factory == null) {
String configFile = "mybatis-config.xml";
InputStream configStream = Resources.getResourceAsStream(configFile);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(configStream);
}
return factory;
}
}
在GetSqlSessionFactory.java类中使用单例设计模式来取得SqlSessionFactory对象。
核心代码如下:
public class GetSqlSession {
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
public static SqlSession getSqlSession() throws IOException {
SqlSession session = tl.get();
if (session == null) {
session = GetSqlSessionFactory.getSqlSessionFactory().openSession();
tl.set(session);
} else {
}
return session;
}
public static void commit() {
if (tl.get() != null) {
tl.get().commit();
tl.get().close();
tl.set(null);
}
}
public static void rollback() {
if (tl.get() != null) {
tl.get().rollback();
tl.get().close();
tl.set(null);
}
}
}
核心代码如下:
public interface UserinfoMapper {
public void insert(Userinfo userinfo);
public void deleteById(long id);
public void updateById(Userinfo userinfo);
public List<Userinfo> selectAll();
public Userinfo selectById(long id);
}
代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"mybatis-3-mapper.dtd">
<mapper namespace="sqlmapping.UserinfoMapper">
<insert id="insert" parameterType="entity.Userinfo">
<selectKey order="BEFORE" resultType="java.lang.Long"
keyProperty="id">
select idauto.nextval from dual
</selectKey>
insert into userinfo(id,username,password,age,insertdate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</insert>
<select id="selectAll" resultType="entity.Userinfo">
select * from userinfo order
by id asc
</select>
<select id="selectById" parameterType="long"
resultType="entity.Userinfo">
select *
from userinfo where id=#{id}
</select>
<update id="updateById" parameterType="entity.Userinfo">
update userinfo set
username=#{username},
password=#{password},
age=#{age},
insertdate=#{insertdate}
where id=#{id}
</update>
<delete id="deleteById" parameterType="long">
delete
from userinfo where
id=#{id}
</delete>
</mapper>
首先测试多次获取的SqlSession对象是不是同一个,核心代码如下:
public class Test1 {
public static void main(String[] args) throws IOException {
System.out.println(GetSqlSession.getSqlSession());
System.out.println(GetSqlSession.getSqlSession());
System.out.println(GetSqlSession.getSqlSession());
System.out.println(GetSqlSession.getSqlSession());
System.out.println(GetSqlSession.getSqlSession());
System.out.println(GetSqlSession.getSqlSession());
}
}
程序运行后在控制台输出的信息如下:
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
org.apache.ibatis.session.defaults.DefaultSqlSession@13deb50e
这说明获得的SqlSession对象是同一个。
添加记录,代码如下:
public class Insert1 {
public static void main(String[] args) throws IOException {
SqlSession session = null;
try {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国111");
userinfo.setPassword("中国人111");
userinfo.setAge(100L);
userinfo.setInsertdate(new Date());
session = GetSqlSession.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
mapper.insert(userinfo);
} catch (Exception e) {
e.printStackTrace();
GetSqlSession.rollback();
} finally {
GetSqlSession.commit();
}
}
}
程序运行后,成功地在数据表中增加了一条记录。
再来测试异常回滚的情况,示例代码如下:
public class Insert2 {
public static void main(String[] args) throws IOException {
SqlSession session = null;
try {
Userinfo userinfo1 = new Userinfo();
userinfo1.setUsername("中国111");
userinfo1.setPassword("中国人111");
userinfo1.setAge(100L);
userinfo1.setInsertdate(new Date());
Userinfo userinfo2 = new Userinfo();
userinfo2.setUsername("中国111");
userinfo2.setPassword(
"中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111中国人111");
userinfo2.setAge(100L);
userinfo2.setInsertdate(new Date());
session = GetSqlSession.getSqlSession();
UserinfoMapper mapper = session.getMapper(UserinfoMapper.class);
mapper.insert(userinfo1);
mapper.insert(userinfo2);
} catch (Exception e) {
e.printStackTrace();
GetSqlSession.rollback();
} finally {
GetSqlSession.commit();
}
}
}
程序运行后在控制台输出的异常信息如下:
Caused by: java.sql.SQLException: ORA-12899: 列 "Y2"."USERINFO"."PASSWORD" 的值太大 (实际值: 261, 最大值: 50)
程序出现异常,并且已经回滚,这是因为数据表userinfo中记录数量不变。其他业务方法不再重复进行测试。
ORM框架MyBatis介绍到这里,读者应该能熟练地使用它进行数据库的CURD操作。