- Java EE核心框架实战
- 高洪岩
- 214字
- 2020-06-27 09:36:34
第1章 MyBatis 3操作数据库
本章将和大家一起分享MyBatis 3框架,此框架的主要作用就是更加便携地操作数据库,比如将数据库返回的内容进行List或实体类的封装,将执行操作的SQL语句配置到XML文件中,这样做有利于代码的后期维护,使代码的分层更加明确。MyBatis框架还具有优化查询效率的缓存等功能。那么在本章中,读者应该着重掌握如下内容:
· 使用基于Eclipse的MyBatis插件执行CURD增删改查操作;
· 使用MyBatis操作常用数据库Oracle、MySQL、MsSQL;
· MyBatis框架中核心对象的生命周期;
· MyBatis结合ThreadLocal类进行CURD的封装。
1.1 MyBatis介绍
为什么要使用MyBatis框架呢?举一个最简单的例子,在使用传统的JDBC代码时,需要写上必要的DAO层代码,在DAO层代码中将数据表中的数据封装到自定义的实体类中。这给代码的维护带来了问题。但MyBatis和Hibernate解决了这样的问题,使用它们做查询时,可以自动地将数据表中的数据记录封装到实体或Map中,再将它们放入List中返回。这么常见的功能都可以由MyBatis和Hibernate自由方便地实现,可见,使用这两个框架开发应用软件会非常方便快捷。
MyBatis是一个持久化框架,它有不同的语言版本,比如.NET和Java都有MyBatis对应的类库;它有大多数ORM框架都具有的功能,比如程序员自定义的SQL语句、调用存储过程和一些高级的映射。但在这里需要说明的是,它是一种半自动化的ORM映射框架,所以使用方式和Hibernate有非常大的区别。它以SQL语句为映射基础,在使用MyBatis框架时,可以将SQL语句灵活多变的特性融入项目开发中。
另外,如果使用MyBatis这个框架,还可以省略大多数的JDBC代码,因为它把常用的JDBC操作都进行了封装,可以加快开发效率。MyBatis可以使用XML或Annotations注解的方式将数据表中的记录映射成一个Map或Java POJO实体对象,这也是现在流行ORM的技术方向。比如Hibernate和大多数JPA规范实现者都可以使用Annotations注解的方式来设计程序。
由于MyBatis框架是第三方软件,因此必须单独进行下载,下载的网址为:
http://code.google.com/p/mybatis/
打开网页后看到如图1-1所示的界面。
图1-1 MyBatis官方网站
继续操作,单击Downloads链接,打开如图1-2所示的界面。
图1-2 准备下载MyBatis框架
此刻已经把MyBatis框架从官网下载到本地计算机中,然后就可以使用它的jar文件进行开发了。下载的zip文件包含开发PDF文档及源代码和jar文件。
1.2 MyBatis操作数据库的步骤
开门见山永远是快速学习一门技术最好的方式。
MyBatis框架的核心是SqlSessionFactory对象,从SqlSessionFactory类的名称来看,它是创建SqlSession对象的工厂。但SqlSessionFactory对象的创建来自于SqlSessionFactoryBuilder类,也就是使用SqlSessionFactoryBuilder类创建SqlSessionFactory对象。
使用SqlSessionFactoryBuilder类创建SqlSessionFactory对象的方式可以来自于一个XML配置文件,也可以来自于一个实例化的Configuration对象。
1.2.1 使用XML配置文件创建SqlSessionFactory对象
使用XML配置文件方式创建SqlSessionFactory对象的核心代码如下。
package test; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class Test { 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) { // TODO Auto-generated catch block e.printStackTrace(); } } }
上述代码的主要作用就是取得SqlSessionFactory工厂对象。下面测试代码是否能正常创建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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="url" value="jdbc:sqlserver://localhost:1079;databaseName=ghydb" /> <property name="username" value="sa" /> <property name="password" value="" /> </dataSource> </environment> </environments> </configuration>
配置文件mybatis-config.xml中主要定义的就是如何连接数据库,以及连接数据库所必备的username和password及url等参数。
项目结构如图1-3所示。
图1-3 项目结构
加入当时最新版的MyBatis框架的jar包,如图1-4所示。
图1-4 使用当前最新的MyBatis 3.2.2版本
运行程序后并没有出现异常,输出的信息如图1-5所示。
到此,SqlSessionFactory对象成功地从XML配置文件中创建。
图1-5 输出sqlSessionFactory对象
1.2.2 SqlSessionFactoryBuilder和SqlSessionFactory类的结构
SqlSessionFactoryBuilder类的结构如图1-6所示。
图1-6 SqlSessionFactoryBuilder类的结构
SqlSessionFactory类的结构如图1-7所示。
图1-7 SqlSessionFactory类的结构
从图1-6和图1-7中可以看到,两者的类结构中基本上全是重载的方法,主要是为了取得SqlSessionFactory和SqlSession对象。
1.2.3 使用MyBatis Generator工具逆向
在ORM框架MyBatis中,实现数据表与JavaBean映射时,配置的代码比较复杂,这种情况也存在于Hibernate框架中,虽然MyBatis框架实现ORM的原理是使用SQL语句进行映射JavaBean,但映射的代码还是比较繁多,所以为了加快开发效率,MyBatis官方提供一个Eclipse插件,该插件主要的功能就是生成ORM映射所需要的文件,但在目前来看,此插件只支持eclipse-java-indigo-SR2以上的版本,所以笔者在这里下载了eclipse-java-indigo-SR2-win32,在线成功安装了MyBatis Generator插件,下一步就是使用此插件生成ORM映射文件了。
新建一个Java项目,然后在Java项目的src节点上右键单击,新建一个MyBatis生成ORM文件的配置文件,如图1-8所示。
图1-8 创建生成ORM的配置xml文件
单击Next按钮,出现如图1-9所示的界面。
在图1-9界面中,不需要更改配置,保持默认设置即可。单击Finish按钮完成配置文件的创建。
图1-9 将文件放入src路径下即可
对生成的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="generatorJava"> <jdbcConnection driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://localhost:1079;databaseName=ghydb" userId="sa" password="" /> <javaModelGenerator targetPackage="orm" targetProject="generatorJava" /> <sqlMapGenerator targetPackage="orm" targetProject="generatorJava" /> <javaClientGenerator targetPackage="orm" targetProject="generatorJava" type="XMLMAPPER" /> <table schema="dbo" tableName="userinfo"> </table> </context> </generatorConfiguration>
配置文件generatorConfig.xml是MyBatis Generator插件中必备的文件,通过此文件可以从数据表的结构逆向出对应的Java类,然后用MyBatis的API就可以对这些Java类进行操作,从而演变成对数据表的增删改查操作。
数据表userinfo的表结构如图1-10所示。
图1-10 userinfo数据表的结构
注意 需要在Java项目中添加sql驱动jar包。
准备就绪后,单击图1-11中的Generate My Batis/i BATIS Artifacts菜单。
图1-11 根据XML配置文件生成orm映射文件
成功生成orm映射文件后,项目结构如图1-12所示。
图1-12 成功生成orm映射文件
把orm包中的内容复制到MyEclipse中Web项目的src路径中,如图1-13所示。
图1-13 MyEclipse中Web项目的结构
在Web项目中添加MyBatis需要的jar文件。
至此,MyBatis的基础类文件已经准备完毕。
1.2.4 使用SqlSession对象在MsSql数据库中新建记录
本节开始把一条记录插入userinfo数据表中。
在Web项目的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.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="url" value="jdbc:sqlserver://localhost:1079;databaseName=ghydb" /> <property name="username" value="sa" /> <property name="password" value="" /> </dataSource> </environment> </environments> <mappers> <mapper resource="orm/UserinfoMapper.xml" /> </mappers> </configuration>
Servlet核心代码如下。
package controller; import java.io.IOException; import java.io.InputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import orm.Userinfo; public class test extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Userinfo userinfo = new Userinfo(); userinfo.setUsername("usernameValue"); userinfo.setPassword("passwordValue"); String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); SqlSession sqlSsession = sqlSessionFactory.openSession(); sqlSsession.insert("insert", userinfo); sqlSsession.commit(); sqlSsession.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Servlet中的代码实现一个经典的insert数据表的功能,从代码中可以看到MyBatis用最精简的API就可以完全控制数据表中的记录,可见不管从学习、开发等方面MyBatis的成本都比较低。
运行程序后,在控制台输出如下异常信息:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 当IDENTITY_INSERT设置为OFF时,不能为表'userinfo'中的标识列插入显式值。
通过信息可以知道,MyBatis并没有识别userinfo数据表中主键id是自增的情况,解决办法很简单,回到eclipse,将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="generatorJava"> <jdbcConnection driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://localhost:1079;databaseName=ghydb" userId="sa" password="" /> <javaModelGenerator targetPackage="orm" targetProject="generatorJava" /> <sqlMapGenerator targetPackage="orm" targetProject="generatorJava" /> <javaClientGenerator targetPackage="orm" targetProject="generatorJava" type="XMLMAPPER" /> <table schema="dbo" tableName="userinfo"> <generatedKey column="id" sqlStatement="sqlserver" identity="true" /> </table> </context> </generatorConfiguration>
这里添加了关键的配置代码:
<generatedKey column="id" sqlStatement="sqlserver" identity="true" />
定义主键值id是自增的。
将生成的orm包中所有内容再次复制到MyEclipse的Web项目中,再次运行Servlet,成功在数据表中添加了一条记录,如图1-14所示。
图1-14 成功添加一条记录
1.2.5 使用SqlSession对象在Oracle数据库中新建记录
回到Eclipse,按照以下代码改动配置文件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="generatorJava"> <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver" connectionURL="jdbc:oracle:thin:@localhost:1522:accp11g" userId="ghy" password="123" /> <javaModelGenerator targetPackage="orm" targetProject="generatorJava" /> <sqlMapGenerator targetPackage="orm" targetProject="generatorJava" /> <javaClientGenerator targetPackage="orm" targetProject="generatorJava" type="XMLMAPPER" /> <table schema="ghy" tableName="userinfo"> <generatedKey column="id" sqlStatement="select idauto.nextval from dual" identity="false" /> </table> </context> </generatorConfiguration>、
关键配置是如下代码。
<generatedKey column="id" sqlStatement="select idauto.nextval from dual" identity="false" />
由于Oracle数据库使用序列来获取ID主键值,因此不配置上述代码在运行时会出现异常。
用Eclipse生成orm的映射文件,并复制到MyEclipse中的Web项目中,运行Servlet,在数据表中插入了新记录,并且id值由序列生成,结果如图1-15所示。
图1-15 Oracle数据表中的新记录
1.3 使用MyBatis针对3种数据库(Oracle、MSSQL和MySQL)实现CURD
前面都是使用MyBatis Generator工具生成实体和SQL映射文件,并不能从基础上讲述MyBatis框架的使用,本节将从零基础开始研究如何使用MyBatis框架针对3种主流数据库实现经典功能CURD。
1.3.1 针对Oracle的CURD
MyBatis框架针对每一种数据库的操作都大同小异,本节将用示例演示3种主流数据库的CURD操作。
1.创建userinfo数据表
创建userinfo数据表,表结构如图1-16所示。
图1-16 userinfo数据表的结构
2.从Eclipse产生逆向的实体类
在Eclipse中新建java项目,名称为oracleGenerator,添加MyBatis配置文件generator Config.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="oracleGenerator"> <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver" connectionURL="jdbc:oracle:thin:@localhost:1522:accp11g" userId="ghy" password="123" /> <javaModelGenerator targetPackage="orm" targetProject="oracleGenerator" /> <sqlMapGenerator targetPackage="orm" targetProject="oracleGenerator" /> <javaClientGenerator targetPackage="orm" targetProject="oracleGenerator" type="XMLMAPPER" /> <table schema="ghy" tableName="userinfo"> <generatedKey column="id" sqlStatement="select idauto.nextval from dual" identity="false" /> </table> </context> </generatorConfiguration>
根据此配置文件会生成实体类Userinfo.java。
3.创建Web项目并配置基本开发环境
回到MyEclipse,创建一个Web项目,命名为mybatis_curd_oracle,将Eclipse项目oracle Generator中的orm包中的Userinfo.java复制到MyEclipse项目中的src路径下,如图1-17所示。
图1-17 MyEclipse中的orm包中有实体
实体类Userinfo.java的类结构如图1-18所示。
图1-18 Userinfo.java的类结构
在Web项目mybatis_curd_oracle中的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.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1522:accp11g" /> <property name="username" value="ghy" /> <property name="password" value="123" /> </dataSource> </environment> </environments> <mappers> <mapper resource="userinfoMapping.xml" /> </mappers> </configuration>
其中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="orm.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> <select id="getUserinfoById" parameterType="int" resultType="orm.Userinfo"> select * from userinfo where id=#{id} </select> <delete id="deleteUserinfoById" parameterType="int"> delete from userinfo where id=#{id} </delete> <select id="getAllUserinfo" resultType="orm.Userinfo"> select * from userinfo </select> <update id="updateUserinfoById" parameterType="orm.Userinfo"> update userinfo set username=#{username},password=#{password},age=#{age},insertDate=#{insertdate} where id=#{id} </update> </mapper>
需要特别说明的是,如果SQL语句有一些特殊字段,则必须按照如下格式使用SQL语句。
<![CDATA[ sql语句 ]]>
其中配置代码如下所示。
<selectKey resultType="java.lang.Long" keyProperty="id" order="BEFORE"> select idauto.nextval from dual </selectKey>
主要的功能是根据序列对象生成一个主键id值,并且此值还可以从代码中获取,也就是插入一条记录后代码就可以获取刚才插入记录的id值。
属性parameterType定义参数的类型,属性resultType定义返回值的类型。
继续创建测试用的Servlet对象,完整的项目结构如图1-19所示。
在图1-19中可以发现src路径下两2个dtd文件,这样为了在开发xml配置或xml映射文件时实现代码自动提示功能。
图1-19 完整的项目结构
4.创建获取SqlSession对象的工具类
获取SqlSession对象的工具类的核心代码如下。
package dbtools; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public abstract class GetSqlSession { public static SqlSession getSqlSession() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); SqlSession sqlSsession = sqlSessionFactory.openSession(); return sqlSsession; } }
5.插入多条记录
创建Servlet,核心代码如下。
public class insertUserinfo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Userinfo userinfo = new Userinfo(); userinfo.setUsername("高洪岩"); userinfo.setPassword("岩洪高"); userinfo.setAge(100L); userinfo.setInsertdate(new Date()); SqlSession sqlSession = GetSqlSession.getSqlSession(); sqlSession.insert("mybatis.testcurd.insertUserinfo", userinfo); System.out.println(userinfo.getId()); sqlSession.commit(); sqlSession.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
变量sqlSession的insert方法的第一个参数是userinfoMapping.xml映射文件<insert>标签的id值,还要加上namespace命名空间的前缀,映射文件的部分代码如下。
<?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" ……>
在代码中可以看到获取已经插入数据表中记录的主键值。
执行Servlet后在控制台输出的结果如图1-20所示。
图1-20 插入多条记录
Oracle数据库中userinfo数据表的内容如图1-21所示。
图1-21 出现3条数据记录
6.根据id值查询记录
创建Servlet,核心代码如下。
public class getUserinfoById extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { SqlSession sqlSession = GetSqlSession.getSqlSession(); Userinfo userinfo = sqlSession.selectOne( "mybatis.testcurd.getUserinfoById", 7); System.out.println(userinfo.getId()); System.out.println(userinfo.getUsername()); System.out.println(userinfo.getPassword()); System.out.println(userinfo.getAge()); System.out.println(userinfo.getInsertdate().toLocaleString()); sqlSession.commit(); sqlSession.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
程序运行后的结果如图1-22所示。
图1-22 输出id是7的信息
7.查询所有记录
创建Servlet,核心代码如下。
public class getAllUserinfo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { SqlSession sqlSession = GetSqlSession.getSqlSession(); List<Userinfo> listUserinfo = sqlSession .selectList("mybatis.testcurd.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().toLocaleString()); } sqlSession.commit(); sqlSession.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
程序运行后输出3条记录,如图1-23所示。
图1-23 输出3条记录信息
8.更新记录
创建Servlet,核心代码如下。
public class updateUserinfoById extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { SqlSession sqlSession = GetSqlSession.getSqlSession(); Userinfo userinfo = sqlSession.selectOne( "mybatis.testcurd.getUserinfoById", 7); userinfo.setUsername("最新版高洪岩"); sqlSession.update("mybatis.testcurd.updateUserinfoById", userinfo); sqlSession.commit(); sqlSession.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
程序运行后数据表userinfo中的记录已更新,如图1-24所示。
图1-24 userinfo数据表中内容已更新
9.删除记录
创建Servlet,核心代码如下。
public class deleteUserinfoById extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { SqlSession sqlSession = GetSqlSession.getSqlSession(); sqlSession.delete("mybatis.testcurd.deleteUserinfoById", 6); sqlSession.commit(); sqlSession.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
程序运行后,将id为6的记录删除了,如图1-25所示。
图1-25 无id为6的记录
到此,针对Oracle数据库的CURD操作到此结束。
1.3.2 针对MSSQL的CURD
操作MSSQL数据库和操作Oracle数据库的大体步骤相同,在SQL Server数据库中创建数据表,如图1-26所示。
图1-26 数据表的结构
1.使用Eclipse逆向实体类
用相同的方法在Eclipse中创建Java项目,命名为mssqlGenerator,添加MyBatis生成文件,配置内容如下。
<?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="mssqlGenerator"> <jdbcConnection driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://localhost:1079;databaseName=ghydb" userId="sa" password="" /> <javaModelGenerator targetPackage="orm" targetProject="mssqlGenerator" /> <sqlMapGenerator targetPackage="orm" targetProject="mssqlGenerator" /> <javaClientGenerator targetPackage="orm" targetProject="mssqlGenerator" type="XMLMAPPER" /> <table schema="dbo" tableName="userinfo"> <generatedKey column="id" sqlStatement="sqlserver" identity="true" /> </table> </context> </generatorConfiguration>
关键代码如下。
<generatedKey column="id" sqlStatement="sqlserver" identity="true" />
它的作用是定义userinfo数据表中的id字段是自增的,不需要显式地设置值。
2.创建Web项目并搭建基本的开发环境
在MyEclipse中创建Web项目,复制Userinfo.java到Web项目中,并且创建一个连接数据库的配置文件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.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="url" value="jdbc:sqlserver://localhost:1079;databaseName=ghydb" /> <property name="username" value="sa" /> <property name="password" value="" /> </dataSource> </environment> </environments> <mappers> <mapper resource="userinfoMapping.xml" /> </mappers> </configuration>
还要创建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="orm.Userinfo" useGeneratedKeys="true" keyProperty="id"> insert into userinfo(username,password,age,insertDate) values(#{username},#{password},#{age},#{insertdate}) </insert> <select id="getUserinfoById" parameterType="int" resultType="orm.Userinfo"> select * from userinfo where id=#{id} </select> <delete id="deleteUserinfoById" parameterType="int"> delete from userinfo where id=#{id} </delete> <select id="getAllUserinfo" resultType="orm.Userinfo"> select * from userinfo </select> <update id="updateUserinfoById" parameterType="orm.Userinfo"> update userinfo set username=#{username},password=#{password},age=#{age},insertDate=#{insertdate} where id=#{id} </update> </mapper>
3.添加记录并且返回主键值
创建Servlet对象,插入记录的代码和Oracle数据库对应的Servlet代码一致,运行后在控制台输出3条记录的主键ID值,结果如图1-27所示。
图1-27 向mssql数据库中插入3条记录
数据表userinfo中的内容如图1-28所示。
图1-28 userinfo数据表中的内容
4.其他业务方法的测试
其他业务方法的代码和操作Oracle数据库的大体一致,并且已经成功运行,具体的代码请参见本书中的源代码。
1.3.3 针对MySQL的CURD
在MySQL数据库中创建数据表userinfo,表结构如图1-29所示。
图1-29 userinfo数据表的结构
一定要勾选Auto Increment复选框,以使字段id值实现自增的效果。
1.使用Eclipse逆向实体类
在Eclipse中创建Java项目,添加逆向配置文件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="mysqlGenerator"> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3307/ghydb" userId="root" password="123" /> <javaModelGenerator targetPackage="orm" targetProject="mysqlGenerator" /> <sqlMapGenerator targetPackage="orm" targetProject="mysqlGenerator" /> <javaClientGenerator targetPackage="orm" targetProject="mysqlGenerator" type="XMLMAPPER" /> <table schema="ghydb" tableName="userinfo"> <generatedKey column="id" sqlStatement="mysql" identity="true" /> </table> </context> </generatorConfiguration>
2.创建Web项目并搭建基本的开发环境
在MyEclipse中创建Web项目,命名为mybatis_curd_mysql,复制Userinfo.java实体类,并且创建一个连接数据库的配置文件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:3307/ghydb" /> <property name="username" value="root" /> <property name="password" value="123" /> </dataSource> </environment> </environments> <mappers> <mapper resource="userinfoMapping.xml" /> </mappers> </configuration>
还要创建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="orm.Userinfo" useGeneratedKeys="true" keyProperty="id"> insert into userinfo(username,password,age,insertDate) values(#{username},#{password},#{age},#{insertdate}) </insert> <select id="getUserinfoById" parameterType="int" resultType="orm.Userinfo"> select * from userinfo where id=#{id} </select> <delete id="deleteUserinfoById" parameterType="int"> delete from userinfo where id=#{id} </delete> <select id="getAllUserinfo" resultType="orm.Userinfo"> select * from userinfo </select> <update id="updateUserinfoById" parameterType="orm.Userinfo"> update userinfo set username=#{username},password=#{password},age=#{age},insertDate=#{insertdate} where id=#{id} </update> </mapper>
MySQL和MSSQL数据库生成主键的映射代码一致。
3.添加记录并且返回主键值
创建Servlet对象,插入记录的代码和Oracle数据库对应的Servlet代码一致,运行后在控制台输出3条记录的主键ID值,结果如图1-30所示。
图1-30 向mysql数据库中插入4条记录
数据表userinfo中的内容如图1-31所示。
图1-31 数据表userinfo中的4条记录
4.其他业务方法的测试
其他业务方法的代码和操作Oracle数据库的大体一致,并且已经成功运行,具体的代码请参见本书中的源代码。
1.4 MyBatis核心对象的生命周期与封装
在前面对3种数据库实现基本的CURD后,读者应该了解了MyBatis核心对象的使用,本节将会继续介绍这些核心对象的生命周期。
对象的生命周期也就是对象从创建到销毁的过程,但在此过程中,如果实现的代码质量不佳,那么很容易造成程序上的错误或效率的降低。
(1)SqlSessionFactoryBuilder对象可以被JVM虚拟机所实例化、使用或者销毁。一旦使用SqlSessionFactoryBuilder对象创建SqlSessionFactory后,SqlSessionFactoryBuilder类就不需要存在了,也就是,不需要保持此对象的状态,可以随意地任由JVM销毁。因此SqlSession FactoryBuilder对象的最佳使用范围是方法之内,也就是说,可以在方法内部声明SqlSessionFactoryBuilder对象来创建SqlSessionFactory对象。
(2)SqlSessionFactory对象由SqlSessionFactoryBuilder对象创建。一旦创建SqlSessionFactory类的实例,该实例应该在应用程序执行期间都存在,根本不需要每一次操作数据库时都重新创建它,所以应用它的最佳方式就是写一个单例模式,或使用Spring框架来实现单例模式对SqlSessionFactory对象进行有效的管理。
(3)SqlSession对象由SqlSessionFactory类创建,需要注意的是,每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享,它也是线程不安全的,所以千万不要在Servlet中声明该对象的一个实例变量。因为Servlet是单例的,声明成实例变量会造成线程安全问题,也绝不能将SqlSession实例的对象放在一个类的静态字段甚至是实例字段中,还不可以将SqlSession实例的对象放在任何类型的管理范围中,比如Servlet对象中的HttpSession会话。在接收到HTTP请求时,可以打开一个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) { e.printStackTrace(); } finally { sqlSession.close(); } } }
1.4.1 创建GetSqlSessionFactory.java类
根据前面学习过的生命周期的知识,在后面的章节中将对MyBatis核心代码进行封装,这样更有助于对数据执行CURD操作。创建Web项目,命名为mybatis_threadlocal。
创建GetSqlSessionFactory.java类,完整代码如下。
package dbtools; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class GetSqlSessionFactory { private static SqlSessionFactory sqlSessionFactory; private GetSqlSessionFactory() { } synchronized public static SqlSessionFactory getSqlSessionFactory() { try { if (sqlSessionFactory == null) { String resource = "mybatis-config.xml"; InputStream inputStream = Resources .getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); } else { } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return sqlSessionFactory; } }
在GetSqlSessionFactory.java类中使用单例的设计模式来获取SqlSessionFactory对象。
1.4.2 创建GetSqlSession.java类
创建GetSqlSession.java类的核心代码如下。
package dbtools; import org.apache.ibatis.session.SqlSession; public class GetSqlSession { private static ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>(); public static SqlSession getSqlSession() { SqlSession sqlSession = tl.get(); if (sqlSession == null) { sqlSession = GetSqlSessionFactory.getSqlSessionFactory() .openSession(); tl.set(sqlSession); } else { } System.out.println("获得的sqlSession对象的hashCode:" + sqlSession.hashCode()); return sqlSession; } public static void commit() { if (tl.get() != null) { tl.get().commit(); tl.get().close(); tl.set(null); System.out.println("提交了"); } } public static void rollback() { if (tl.get() != null) { tl.get().rollback(); tl.get().close(); tl.set(null); System.out.println("回滚了"); } } }
1.4.3 创建DBOperate.java类
创建DBOperate.java类的核心代码如下。
package dbtools; import java.util.List; import java.util.Map; import org.apache.ibatis.session.SqlSession; public class DBOperate { public int insert(String sql, Map valueMap) { SqlSession sqlSession = GetSqlSession.getSqlSession(); return sqlSession.insert(sql, valueMap); } public int delete(String sql, Map valueMap) { SqlSession sqlSession = GetSqlSession.getSqlSession(); return sqlSession.delete(sql, valueMap); } public int update(String sql, Map valueMap) { SqlSession sqlSession = GetSqlSession.getSqlSession(); return sqlSession.update(sql, valueMap); } public List<Map> select(String sql, Map valueMap) { SqlSession sqlSession = GetSqlSession.getSqlSession(); return sqlSession.selectList(sql, valueMap); } }
所有CURD的参数值都用Map对象进行封装,所以要查看SQL映射文件中的代码。
1.4.4 创建userinfoMapping.xml映射文件
创建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="map" useGeneratedKeys="true" keyProperty="id"> insert into userinfo(username,password,age,insertDate) values(#{username},#{password},#{age},#{insertdate}) </insert> <select id="getUserinfoById" parameterType="map" resultType="map"> select * from userinfo where id=#{id} </select> <delete id="deleteUserinfoById" parameterType="map"> delete from userinfo where id=#{id} </delete> <select id="getAllUserinfo" resultType="map"> select * from userinfo </select> <update id="updateUserinfoById" parameterType="map"> update userinfo set username=#{username},password=#{password},age=#{age},insertDate=#{insertdate} where id=#{id} </update> </mapper>
1.4.5 创建连接数据库的mybatis-config.xml配置文件
创建连接数据库的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.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="url" value="jdbc:sqlserver://localhost:1079;databaseName=ghydb" /> <property name="username" value="sa" /> <property name="password" value="" /> </dataSource> </environment> </environments> <mappers> <mapper resource="userinfoMapping.xml" /> </mappers> </configuration>
1.4.6 创建名为test的Servlet对象
该对象的主要作用就是测试在一个请求中多次获取的SqlSession对象是不是同一个,核心代码如下。
public class test extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { GetSqlSession.getSqlSession(); GetSqlSession.getSqlSession(); GetSqlSession.getSqlSession(); GetSqlSession.getSqlSession(); GetSqlSession.getSqlSession(); } }
运行程序后,在控制台输出的信息如图1-32所示。
图1-32 获得的SqlSession对象是同一个
1.4.7 添加记录及异常回滚的测试
添加记录及异常回滚的测试,核心代码如下。
public class insert extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { HashMap valueMap1 = new HashMap(); valueMap1.put("username", "高洪岩今天1"); valueMap1.put("password", "高洪岩明天1"); valueMap1.put("age", 100); valueMap1.put("insertdate", new Date()); HashMap valueMap2 = new HashMap(); valueMap2.put("username", "高洪岩今天2"); valueMap2.put("password", "高洪岩明天2"); valueMap2.put("age", 100); valueMap2.put("insertdate", new Date()); DBOperate dbo = new DBOperate(); dbo.insert("insertUserinfo", valueMap1); dbo.insert("insertUserinfo", valueMap2); } catch (Exception e) { e.printStackTrace(); GetSqlSession.rollback(); } finally { GetSqlSession.commit(); } } }
运行程序后,在控制台输出的信息如图1-33所示。
图1-33 控制台输出的信息
数据表中的数据如图1-34所示。
图1-34 成功添加两条记录
上面的步骤证明添加多条记录成功,userinfo数据表中有两条记录。再来测试异常回滚的情况,更改部分的代码如下。
HashMap valueMap2 = new HashMap(); valueMap2.put("username", "高洪岩今天2_123456789_123456789_123456789_123456789_123456789"); valueMap2.put("password", "高洪岩明天2"); valueMap2.put("age", 100); valueMap2.put("insertdate", new Date());
运行程序后,在控制台输出的异常信息如下。
获得的sqlSession对象的hashCode:24442607 获得的sqlSession对象的hashCode:24442607 org.apache.ibatis.exceptions.PersistenceException: ### Error updating database. Cause: com.microsoft.sqlserver.jdbc.SQLServerException: 将 截断字符串或二进制数据。 ### The error may involve mybatis.testcurd.insertUserinfo-Inline ### The error occurred while setting parameters ### SQL: insert into userinfo(username,password,age,insertDate) values(?,?,?,?) ### Cause: com.microsoft.sqlserver.jdbc.SQLServerException: 将截断字符串或二进制数据。 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory. java:23) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession. java:147) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession. java:134) at dbtools.DBOperate.insert(DBOperate.java:12) at controller.insert.doGet(insert.java:36) at javax.servlet.http.HttpServlet.service(HttpServlet.java:690) rotocol.java:624) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445) at java.lang.Thread.run(Thread.java:619) Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 将截断字符串或二进制数据。 at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession. java:145) ... 17 more 回滚了
通过上面的信息可以得知,程序出现异常,并且已经回滚,那userinfo数据表中是否还存在两条记录呢?查看userinfo数据表,其内容如图1-35所示。
图1-35 成功回滚后还是两条记录
在userinfo数据表中多添增几条记录,便于后面的测试,新增的记录如图1-36所示。
图1-36 userinfo表中的多条记录
1.4.8 删除记录
删除记录,核心代码如下。
public class delete extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { HashMap valueMap1 = new HashMap(); valueMap1.put("id", 44); DBOperate dbo = new DBOperate(); dbo.delete("deleteUserinfoById", valueMap1); } catch (Exception e) { e.printStackTrace(); GetSqlSession.rollback(); } finally { GetSqlSession.commit(); } } }
运行程序后,userinfo数据表中的记录如图1-37所示。
图1-37 成功删除id为44的记录
1.4.9 更改记录
更改记录,核心代码如下。
public class update extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { HashMap valueMap1 = new HashMap(); valueMap1.put("id", 45); valueMap1.put("username", "高洪岩今天3new"); valueMap1.put("password", "高洪岩明天3new"); valueMap1.put("age", 100); valueMap1.put("insertdate", new Date()); DBOperate dbo = new DBOperate(); dbo.update("updateUserinfoById", valueMap1); } catch (Exception e) { e.printStackTrace(); GetSqlSession.rollback(); } finally { GetSqlSession.commit(); } } }
运行程序后,userinfo表中的数据如图1-38所示。
图1-38 userinfo表中的记录
1.4.10 查询单条记录
查询单条记录,核心代码如下。
public class getUserinfoById extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { HashMap valueMap1 = new HashMap(); valueMap1.put("id", 39); DBOperate dbo = new DBOperate(); List<Map> list = dbo.select("getUserinfoById", valueMap1); for (int i = 0; i < list.size(); i++) { Map rowMap = list.get(i); System.out.println(rowMap.get("id") + "_" + rowMap.get("username") + "_" + rowMap.get("password") + "_" + rowMap.get("age") + "_" + rowMap.get("insertDate")); } } catch (Exception e) { e.printStackTrace(); GetSqlSession.rollback(); } finally { GetSqlSession.commit(); } } }
运行程序后,在控制台输出如图1-39所示的结果。
图1-39 控制台输出的信息
1.4.11 查询多条记录
查询多条记录,核心代码如下。
public class getAllUserinfo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { DBOperate dbo = new DBOperate(); List<Map> list = dbo.select("getAllUserinfo", null); for (int i = 0; i < list.size(); i++) { Map rowMap = list.get(i); System.out.println(rowMap.get("id") + "_" + rowMap.get("username") + "_" + rowMap.get("password") + "_" + rowMap.get("age") + "_" + rowMap.get("insertDate")); } } catch (Exception e) { e.printStackTrace(); GetSqlSession.rollback(); } finally { GetSqlSession.commit(); } } }
运行程序后,控制台的输出如图1-40所示。
图1-40 控制台输出的多条记录信息
ORM框架MyBatis介绍到这里,读者应该能熟练地使用它进行数据库的CURD操作,并且对核心API在使用上有一个了解。