您的当前位置:首页正文

由“单独搭建Mybatis”到“Mybatis与Spring的整合_集成”

2021-02-19 来源:汇意旅游网
由“单独搭建Mybatis”到“Mybatis与Spring的整合/集成”

在J2EE领域,Hibernate与Mybatis是大家常用的持久层框架,它们各有特点,在持久层框架中处于领导地位。 本文主要介绍Mybatis(对于较小型的系统,特别是报表较多的系统,个人偏向Mybatis),对于它,个人比较喜欢的是: 使用简单、方便;

支持的XML动态SQL的编写,方便浏览、修改,同时降低SQL与应用程序之间的耦合。 不喜欢的是:

出现错误时,调试不太方便

本文主要介绍Mybatis的搭建,是学习Mybatis过程后整理的札记,其中包括“单独搭建Mybaits”和常用的“Mybatis与Spring的整合”。

一、数据库的准备

因为Mybatis是持久层框架,毫无疑问,是需要操作数据库的。所以,在搭建之前,我们需要先创建一个简单的表。

create table T_USER_TEST_1407 (

USERNAME VARCHAR2(255), PASSWORD VARCHAR2(255) )

SQL - DDL - Create Table

插入一些数据,以作查询的测试。

insert into T_USER_TEST_1407 (USERNAME, PASSWORD) values ('nick', 'Optimistic,Confident,Love - 1'); SQL - DML - Insert table

二、单独搭建Mybaits 1)环境准备、版本说明

此工程使用JDK1.6 + mybatis-3.2.4 + Oracle11g。

新建一个Web工程,由于只构建Mybatis,只引用Mybatis和Oracle JDBC驱动包 mybatis-3.2.4.jar ojdbc6.jar

2)程序的搭建

首先,我们将数据源等配置信息放在一个xml,让Mybatis可以根据这个信息去连接数据库、管理事务。 目前我们可只关注environments节点,此节点是用于配置数据源、事务管理的 。 其他的节点,如typeAliases、mappers,是用于注册一些信息的,后面会陆续提到。 1 2 3 PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" 4 \"://mybatis.org/dtd/mybatis-3-config.dtd\"> 5

6 7

8 9

10 11 12

13

14 15

16 17

18

19 20 21 22 23 24 25

26 27

28

29 30 31

32 mybatis-config.xml

既然有了配置的xml,下一步就需要让Mybatis加载它了。 首先以输入流的形式加载xml

以“SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession”的流程最后构建出SqlSession。

SqlSession,顾名思义,是一次会话,是应用程序与数据库交互的会话,所以,其生命周期应在一次数据库连接之间,当然,此次数据库连接可以包含一次或多次数据库操作。

SqlSessionFactory,顾名思义,是SqlSession的工厂类,用于产出SqlSession。我们知道,SqlSession主要用于数据库操作,而数据库操作又是贯穿于应用程序整个生命周期当中的,那么,\"产出SqlSession\"这个动作也应当贯穿于应用程序整个生命周期当中,所以,SqlSessionFactory的生命周期一般为应用程序的整个生命周期,一般为单例/static的形式存在。

SqlSessionFactoryBuilder,由代码可见,其主要作用是从配置文件中获取配置信息,然后构建SqlSessionFactory,所以其生命周期可以是临时的,局部的。

通过SqlSession获取UserMapper接口,再调用该接口的数据操纵方法。 package com.nicchagil.mybatisonly;

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;

import com.nicchagil.mybatisonly.bean.User;

import com.nicchagil.mybatisonly.mapper.UserMapper;

public class Call {

public static SqlSessionFactory sqlSessionFactory = null;

public static void main(String[] args) throws IOException {

// Query User /*

kickStartMybatis(); queryUser(\"nick\"); */

// Inser User

kickStartMybatis();

insertUser(\"user004\ }

public static void kickStartMybatis() throws IOException {

String resource = \"com/nicchagil/mybatisonly/mybatis-config.xml\"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } /**

* Query User

* @param username * @return */

public static User queryUser(String username) {

User user = null;

SqlSession session = sqlSessionFactory.openSession(); try {

/* Un-recommended Method */ /*

user = (User)session.selectOne(\"com.nicchagil.mybatisonly.mapper.UserMapper.queryUser\ */

/* Recommended Method */

UserMapper userMapper = session.getMapper(UserMapper.class); user = userMapper.queryUser(username);

System.out.println(\"username - \" + user.getUsername() + \" , password - \" + user.getPassword()); } finally {

session.close(); }

return user; } /**

* Insert User

* @param username * @param password */

public static void insertUser(String username, String password) {

SqlSession session = sqlSessionFactory.openSession(); try {

UserMapper userMapper = session.getMapper(UserMapper.class);

User user = new User();

user.setUsername(username); user.setPassword(password);

userMapper.insertUser(user);

// Flushes batch statements and commits database connection.

// Note that database connection will not be committed if no updates/deletes/inserts were called. session.commit();

System.out.println(\"username - \" + user.getUsername() + \" , password - \" + user.getPassword());

} catch (Exception e) { session.rollback(); e.printStackTrace();

//TODO Print the exception logs

//TODO Prompts fail to execute for user } finally {

session.close(); } } /**

* Insert User

* @param username * @param password */

public static void insertUserBySQL(String username, String password) {

SqlSession session = sqlSessionFactory.openSession(); try {

User user = new User();

user.setUsername(username); user.setPassword(password);

session.insert(\"com.nicchagil.mybatisonly.mapper.UserMapper.insertUser\

// Flushes batch statements and commits database connection.

// Note that database connection will not be committed if no updates/deletes/inserts were called. session.commit();

} catch (Exception e) { session.rollback(); e.printStackTrace();

//TODO Print the exception logs

//TODO Prompts fail to execute for user } finally {

session.close(); } } } Call

UserMapper是一个DAO的接口,是定义作哪些数据库操作的。 1 package com.nicchagil.mybatisonly.mapper; 2

3 import com.nicchagil.mybatisonly.bean.User; 4

5 public interface UserMapper { 6

7 public User queryUser(String username); 8 9 }

UserMapper.java

UserMapper只是供调用的接口,那么具体的实现逻辑在哪里呢?

我们可见UserMaper.xml,它定义的SQL就是用于定义UserMapper接口的实现。我们需在mybatis-config.xml注册UserMaper.xml,可见mybatis-config.xml的mappers节点。

我们可以看到id为queryUser,与接口的方法名对应;

SQL我们很熟悉了,就是一个简单的SQL,而#{username},就是接口方法的入参;

resultType为\"user\",这个user是一个别名,具体对应com.nicchagil.mybatisonly.bean.User这个类,我们可以看到在mybatis-config.xml文件的typeAliases节点中已经注册它们的映射关系。 PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"://mybatis.org/dtd/mybatis-3-mapper.dtd\">

INSERT INTO t_user_test_1407 T (T.USERNAME, T.PASSWORD) VALUES (#{username}, #{password})

UserMapper.xml

而com.nicchagil.mybatisonly.bean.User是实体类,用于装载数据。 1 package com.nicchagil.mybatisonly.bean; 2

3 public class User { 4

5 private String username; 6 private String password; 7

8 public String getUsername() { 9 return username;

10 } 11

12 public void setUsername(String username) { 13 this.username = username; 14 } 15

16 public String getPassword() { 17 return password; 18 } 19

20 public void setPassword(String password) { 21 this.password = password; 22 } 23

24 @Override

25 public int hashCode() { 26 final int prime = 31; 27 int result = 1;

28 result = prime * result

29 + ((password == null) ? 0 : password.hashCode()); 30 result = prime * result

31 + ((username == null) ? 0 : username.hashCode()); 32 return result; 33 } 34

35 @Override

36 public boolean equals(Object obj) { 37 if (this == obj) 38 return true; 39 if (obj == null) 40 return false;

41 if (getClass() != obj.getClass()) 42 return false;

43 User other = (User) obj; 44 if (password == null) {

45 if (other.password != null) 46 return false;

47 } else if (!password.equals(other.password)) 48 return false; 49 if (username == null) {

50 if (other.username != null) 51 return false;

52 } else if (!username.equals(other.username)) 53 return false; 54 return true; 55 } 56 57 }

User.java

最后,我们运行Call.java,将能成功查询、插入数据库。我们可通过打印的信息和查询数据库,以查看是否成功查询、插入数据。

3)事务说明

对于数据库有写操作的应用程序,一般来说,事务是不可或缺的一部分。因为未使用其他框架,这里使用编程式事务,即使用SqlSession.commit()和SqlSession.rollback()方法,可见Call.java。

由于本程序对事务有异常回滚的要求,所以,需要获取非自动提交的SqlSession 如程序执行正常,则最后执行session.commit()以提交事务。

session.commit()有个需注意的地方,参考其如下注释,即如果当前会话中不涉及updates/deletes/insert等写数动作则不提交事务。所以,如果要触发Mybatis提交事务,就需执行明确的触发动作,如“执行session.insert(...)方法”或“执行对应的SQL Mapper配置中的insert、update、delete等标签”等操作。(本人曾尝试在SQL Mapper配置中用select标签包含INSERT的SQL,使用SqlSession.commit()后,执行正常,但没有提交事务,可见并未触发,所以,需规范使用标签)。如需强制提交,可用SqlSession.commit(boolean)。

Flushes batch statements and commits database connection. Note that database connection will not be committed if no updates/deletes/inserts were called. To force the commit call SqlSession.commit(boolean)

如程序执行异常,则回滚事务,session.rollback()

单独搭建Mybaits完毕!

二、 Mybatis与Spring的整合 一个项目中,单独使用Mybatis的情况并不多;更多的情况下,我们需要将Mybatis与其他框架进行整合,以便更好地使用。比如Mybatis + Spring,就是一个流行的整合组合。

1)环境准备、版本说明

本次用Mybatis3 + Spring3进行整合。注意,并不包含MVC框架的配置,因为本文的目的是学习Mybatis,所以尽量不引用其他框架,以避免影响代码的理解。

需引入的类库详情如下:

junit junit 3.8.1 test

org.springframework spring-context 3.2.10.RELEASE

org.springframework.webflow spring-webflow 2.4.0.RELEASE

org.springframework.data spring-data-oracle 1.0.0.RELEASE

org.mybatis mybatis 3.2.4

org.mybatis

mybatis-spring 1.2.2

MVN dependencies

2)程序的搭建

首先,我们在Spring中配置关于Mybatis数据源的信息。

这里以applicationContext-mybatis.xml来体现,配置了如下信息: 注册数据源,常见的有JDBC或JNDI,根据具体情况择一。 注册sqlSessionFactory

sqlSessionFactory是用来生产sqlSession以操作数据库的,所以,需指定sqlSessionFactory所引用的数据源

指定相应的SQL Mapper文件在哪里。我们自命名“_mapper后缀的xml文件”,主要用来定义SQL;“_resultmap后缀的xml文件”,则主要用来定义DB字段与应用程序实体属性的映射。

指定相应的应用程序实体在哪里,并自动注册不包含package名的别名 在哪些package下扫描Mapper接口,即DAO接口

xmlns:context=\"://.springframework.org/schema/context\" xmlns:xsi=\"://.w3.org/2001/XMLSchema-instance\"

xsi:schemaLocation=\"://.springframework.org/schema/beans ://.springframework.org/schema/beans/spring-beans-3.0.xsd

://.springframework.org/schema/context

://.springframework.org/schema/context/spring-context-3.0.xsd\">

JNDI_TEST_DB

classpath:com/nicchagil/mybatis3spring3intg/mapper/sqlxml/*_mapper.xml

classpath:com/nicchagil/mybatis3spring3intg/bean/resultmapxml/*_resultmap.xml

applicationContext-mybatis.xml

除了Mybatis的信息,还有一些Spring的信息需要配置: 根据注解自动扫描并注册bean

Spring的声明式事务管理(用以替代上一章节的“编程式事务”)

由于本程序没有集成MVC框架,在Servlet是通过Spring编程式地获得Spring管理的bean,所以这里注册一个Spring的工具类。(使用了MVC框架并将框架交由Spring IOC容器管理的,可忽视此点配置)

xmlns:context=\"://.springframework.org/schema/context\" xmlns:tx=\"://.springframework.org/schema/tx\" xmlns:xsi=\"://.w3.org/2001/XMLSchema-instance\"

xsi:schemaLocation=\"://.springframework.org/schema/beans ://.springframework.org/schema/beans/spring-beans-3.0.xsd ://.springframework.org/schema/context

://.springframework.org/schema/context/spring-context-3.0.xsd ://.springframework.org/schema/tx

://.springframework.org/schema/tx/spring-tx-3.0.xsd\">

applicationContext.xml

众所周知,以上是Spring的配置文件,那么我们需要告诉应用程序“这些配置文件在哪里”,所以我们需要在web.xml中告诉应用程序。另外,此web.xml注册了一个Servlet,用于接收页面的请求。 xsi:schemaLocation=\"://java.sun.com/xml/ns/javaee ://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\" id=\"WebApp_ID\" version=\"2.5\"> mybatis3spring3Intg

index.html

org.springframework.web.context.ContextLoaderListener

contextConfigLocation

classpath*:config/applicationContext*.xml

UserServlet UserServlet

com.nicchagil.mybatis3spring3intg.servlet.UserServlet

UserServlet /UserServlet web.xml

我们还需要定义Mapper的接口,即DAO接口。此处的Mapper的接口,我们已经在applicationContext-mybatis.xml中注册为指定路径下自动扫描。 package com.nicchagil.mybatis3spring3intg.mapper;

import com.nicchagil.mybatis3spring3intg.bean.User;

public interface UserMapper {

public User find(String username);

public void save(User user); }

UserMapper.java

而Mapper的实现是如何的呢? Mybatis会帮我们实现,我们只需要通过user_mapper.xml文件告诉Mybatis对应的SQL,此处的mapper文件,已经在applicationContext-mybatis.xml中注册为指定路径下自动扫描。

PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"://mybatis.org/dtd/mybatis-3-mapper.dtd\">

INSERT INTO t_user_test_1407 T (T.USERNAME, T.PASSWORD) VALUES (#{username}, #{password})

user_mapper.xml

可以看到,Mapper和SQL配置文件中都引用到了实体类,我们也需要定义。此处的实体类,已经在applicationContext-mybatis.xml中注册为指定路径下自动扫描。

package com.nicchagil.mybatis3spring3intg.bean;

public class User {

private String username; private String password;

private String childhoodName;

public String getUsername() { return username; }

public void setUsername(String username) { this.username = username; }

public String getPassword() { return password; }

public void setPassword(String password) { this.password = password; }

public String getChildhoodName() { return childhoodName; }

public void setChildhoodName(String childhoodName) { this.childhoodName = childhoodName; }

@Override

public int hashCode() { final int prime = 31; int result = 1;

result = prime * result

+ ((childhoodName == null) ? 0 : childhoodName.hashCode()); result = prime * result

+ ((password == null) ? 0 : password.hashCode()); result = prime * result

+ ((username == null) ? 0 : username.hashCode()); return result; }

@Override

public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false;

if (getClass() != obj.getClass()) return false;

User other = (User) obj; if (childhoodName == null) {

if (other.childhoodName != null) return false;

} else if (!childhoodName.equals(other.childhoodName)) return false; if (password == null) {

if (other.password != null) return false;

} else if (!password.equals(other.password))

return false; if (username == null) {

if (other.username != null) return false;

} else if (!username.equals(other.username)) return false; return true; } }

User.java

实体的属性与DB的字段之间的映射/匹配,我们需要定义一下。此处的resultmap.xml文件已经在applicationContext-mybatis.xml中注册为指定路径下自动扫描。

user_resultmap.xml

完成了DAO,那么接着写Service。 首先一个Service的接口。

package com.nicchagil.mybatis3spring3intg.service;

import com.nicchagil.mybatis3spring3intg.bean.User;

public interface UserService {

public User query(String username);

public void save(User user);

public void testTransaction(User user1, User user2); }

UserService.java

Service的实现类如下,这里只简单地测试查询、保存、事务是否能正常处理。

package com.nicchagil.mybatis3spring3intg.service.impl;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import com.nicchagil.mybatis3spring3intg.bean.User;

import com.nicchagil.mybatis3spring3intg.mapper.UserMapper; import com.nicchagil.mybatis3spring3intg.service.UserService;

@Service

public class UserServiceImpl implements UserService {

@Autowired

private UserMapper mapper;

@Override

public User query(String username) { return mapper.find(username); }

@Override

public void save(User user) { mapper.save(user); }

@Override @Transactional

public void testTransaction(User user1, User user2) { mapper.save(user1);

// Code a NullPointerException to test transaction setting String str = null; str.charAt(0);

mapper.save(user2); } }

UserServiceImpl.java

由于没有整合MVC框架,此处由一个Servlet(此Servlet已于web.xml中注册)获取页面请求并调用Service,

那么如何在Servlet中获得Spring IOC管理下Service的bean呢?这里借助SpringContextUtil(implements ApplicationContextAware),此SpringContextUtil于以上提及的applicationContext.xml中注册。 package com.nicchagil.mybatis3spring3intg.servlet;

import java.io.IOException;

import javax.servlet.ServletException; import javax.servlet..Servlet;

import javax.servlet..ServletRequest; import javax.servlet..ServletResponse;

import com.nicchagil.mybatis3spring3intg.bean.User;

import com.nicchagil.mybatis3spring3intg.service.UserService; import com.nicchagil.util.SpringContextUtil; /**

* Servlet implementation class UserServlet */

public class UserServlet extends Servlet {

private static final long serialVersionUID = 1L; /**

* @see Servlet#Servlet() */

public UserServlet() { super();

// TODO Auto-generated constructor stub }

/**

* @see Servlet#doGet(ServletRequest request, ServletResponse response) */

protected void doGet(ServletRequest request, ServletResponse response) throws ServletException, IOException { this.doPost(request, response); }

/**

* @see Servlet#doPost(ServletRequest request, ServletResponse response) */

protected void doPost(ServletRequest request, ServletResponse response) throws ServletException, IOException {

String action = request.getParameter(\"action\");

UserService service = (UserService)SpringContextUtil.getBean(\"userServiceImpl\");

if (\"find\".equals(action)) {

User user = service.query(request.getParameter(\"username\"));

System.out.println(user.getUsername() + \" - \" + user.getPassword() + \" - \" + user.getChildhoodName()); }

if (\"save\".equals(action)) { User user = new User();

user.setUsername(request.getParameter(\"username\")); user.setPassword(request.getParameter(\"password\"));

service.save(user);

System.out.println(user.getUsername() + \" - \" + user.getPassword()); }

if (\"testTransaction\".equals(action)) { User user1 = new User();

user1.setUsername(request.getParameter(\"username\")); user1.setPassword(request.getParameter(\"password\"));

User user2 = new User();

user2.setUsername(request.getParameter(\"username\") + \" - Double\"); user2.setPassword(request.getParameter(\"password\") + \" - Double\");

service.testTransaction(user1, user2);

System.out.println(user1.getUsername() + \" - \" + user1.getPassword()); System.out.println(user2.getUsername() + \" - \" + user2.getPassword()); } } }

UserServlet.java

package com.nicchagil.util;

import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

public class SpringContextUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext = null;

@Override

public void setApplicationContext(ApplicationContext ac) throws BeansException { applicationContext = ac;

}

public static ApplicationContext getApplicationContext() { return applicationContext; }

public static Object getBean(String beanName) { return applicationContext.getBean(beanName); }

public static boolean containsBean(String beanName) { return applicationContext.containsBean(beanName); } }

SpringContextUtil.java

几乎大功告成。

这里写了些触发测试的页面,执行结果可通过“查看控制台”或“查询数据库”获得。哈哈!~~ 导航页

Insert title here

find

save

testTransaction

index.html

输入username查询记录的触发页面

Insert title here

find.html

保存页面

Insert title here

save.html

测试事务的触发页面

Insert title here

testTransaction.html

大功告成!!

因篇幅问题不能全部显示,请点此查看更多更全内容