spring mvc集成mybatis进行数据库访问

一、概述

         这一篇紧接着上一篇spring mvc集成logback日志功能,将mybatis集成进来。之前我们进行用户密码验证采用的是硬编码方式,集成mybatis后,我们就有了数据库访问功能,用户名密码就可以通过访问数据库进行验证了。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
相对于spring自有的orm框架spring data jpa来说,mybatis的优势在于SQL编写的高度自由、以及十分强大的结果集映射。一般中小型项目和简单的crud应用选择jpa,因为它的repository约束较多,带来的好处就是写很少的代码就让其自动生成相应的查询SQL。但是复杂和大型项目,mybatis就显得更适合,原因就在于SQL编写自由、结果集映射强大,但带来的坏处就是庞大、复杂的mapper文件。因为本人mybatis使用得多一些,所以选择了写集成mybatis,有空的话,我会补上spring mvc集成spring data jpa。
另外这里仅仅讲解集成,不对mybatis进行过多介绍,mybatis的详细资料可以参考官方文档:http://www.mybatis.org/mybatis-3/zh/index.html

二、操作步骤

1、添加pom依赖

<!--mybatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.2.7</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.2.2</version>
    </dependency>
    <!--数据源,如果使用jndi则不需要-->
    <dependency>
      <groupId>proxool</groupId>
      <artifactId>proxool</artifactId>
      <version>0.9.1</version>
    </dependency>
    <dependency>
      <groupId>proxool</groupId>
      <artifactId>proxool-cglib</artifactId>
      <version>0.9.1</version>
    </dependency>
    <!--oracle 驱动-->
    <dependency>
      <groupId>com.oracle</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>11.2.2</version>
    </dependency>
依赖里面首先是数据库驱动,我使用的是oracle数据库,所以需要ojdbc驱动包。然后是数据源相关的两个依赖,proxool和proxool-cglib。如果使用的是jndi方式,则不需要这个两个依赖包。接下来是mybatis的两个相关依赖,mybatis是核心包,mybtis-spring是spring集成mybatis需要的依赖包。此外还需要spring的orm依赖:
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>4.2.3.RELEASE</version>
    </dependency>

2、配置数据源

数据源一般不通过硬编码写在xml文件里面,而是通过properties文件进行配置,可灵活改变。在resources目录下新建config目录,并新建一个config_.properties文件,如图所示:

config_.properties文件内容如下:
#数据源配置
jdbc.driverClass=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@xxx.xxx.XXX.XXX:1521:xxxx
jdbc.username=xxx
jdbc.password=xxx
这样我们在进行mybaits相关配置时,就可以直接引用这些内容了。为了能够在xml里通过${}读取properties文件内容,我们还要配置profile。在resources目录新建applicationContext-profile.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<!--properties文件配置-->
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="locations">
			<list>
				<!-- 标准配置 -->
				<value>classpath:config/config_#{systemProperties['run_env']}.properties</value>
			</list>
		</property>
	</bean>

</beans>
l ocations里面列出了配置文件位置,而且通过run_env来读取不同的配置文件。这样就可以在测试环境读取测试环境的配置(例如config_test.properties)、生产环境读取生产环境的配置(例如config_prod.properties),tomcat启动没有定义run_env则使用默认的配置文件config_.properties。

3、配置mybatis

在resources目录下面新建applicationContext-dataSource.xml文件,文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<description>数据源配置</description>

	<beans>
		<!--数据源-->
		<bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
			<property name="driver" value="${jdbc.driverClass}" />
			<property name="driverUrl" value="${jdbc.url}" />
			<property name="user" value="${jdbc.username}" />
			<property name="password" value="${jdbc.password}" />
			<property name="alias" value="Pool_dbname" />
			<property name="maximumActiveTime" value="300000" />
			<property name="prototypeCount" value="0" />
			<property name="maximumConnectionCount" value="10" />
			<property name="minimumConnectionCount" value="2" />
			<property name="simultaneousBuildThrottle" value="10" />
			<property name="houseKeepingTestSql" value="select 1 from dual" />
		</bean>

		<!--mybatis sqlSeesionFactory配置-->
		<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
			<property name="dataSource" ref="dataSource" />
			<property name="configLocation" value="classpath:mybatis-config.xml" />
			<property name="mapperLocations" value="classpath:mapper/*Mapper.xml" />
			<property name="typeAliasesPackage" value="com.xxx.demo.dataaccess.model" />
		</bean>

		<!--mapper自动扫描配置-->
		<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
			<property name="basePackage" value="com.xxx.demo.dataaccess.dao" />
			<!-- 这里不要定义sqlSessionFactory, 定义了会导致properties文件无法加载 -->
		</bean>

		<!--spring 事务配置-->
		<context:component-scan base-package="com.xxx.demo.dataaccess.impl" />
		<bean id="transactionManagerForMybatis" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
			<property name="dataSource" ref="dataSource" />
		</bean>
		<tx:annotation-driven transaction-manager="transactionManagerForMybatis"/>

	</beans>
</beans>

首先是定义DataSource,它表示用于连接数据库的数据源。其中重要的属性我们都放在了config_.properties文件中,通过${}来引用。

其次就是我们的mybatis sqlSessionFactoryBean,在定义SqlSessionFactoryBean的时候,dataSource属性是必须指定的,其它属性的含义如下:

  •  mapperLocations:它表示我们的Mapper文件存放的位置。
  • configLocation:用于指定Mybatis的配置文件位置。如果指定了该属性,那么会以该配置文件的内容作为配置信息构建对应的SqlSessionFactoryBuilder,但是后续属性指定的内容会覆盖该配置文件里面指定的对应内容。我们在resources目录下新建一个mybatis-config.xml文件,不过里面暂时没有什么内容。
  •  typeAliasesPackage:它一般对应我们的实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。
然后我们配置mapper接口的自动扫描,指定basePackage,这样该包下面的mapper接口都会被识别。
最后是事务配置,先自动配置扫描包,让服务bean能够被识别;然后定义事务管理bean,最后启用事务。

配置完成后,在applicationContext.xml文件中,将applicationContext-profile.xml,applicationContext-datasource.xml文件引入:
<import resource="applicationContext-profile.xml" />
	<import resource="applicationContext-datasource.xml"/>
至此,配置告一段落,可以开始进行登录验证的开发了。

4、数据库访问

mybatis的数据库访问开发步骤都大同小异,都是定义好model,dao,然后写mapper文件。为了减少重复工作,mybatis提供了一个工具包mybatis-generator,可以帮我们根据表自动生成model、dao和mapper文件,包含了通用的增删改查代码。这个只是个额外的帮助工具,限于篇幅不做过多介绍,可以参考这篇文章:Intellij IDEA 中使用 MyBatis-generator 自动生成 MyBatis 代码

表结构

在数据库创建如下表:
--测试表
DROP TABLE USER_TEST CASCADE CONSTRAINTS;

CREATE TABLE USER_TEST
(
  ID               VARCHAR2(32)  NOT NULL,
  LOGIN_NAME       VARCHAR2(10),
  PASSWORD         VARCHAR2(128),
  REAL_NAME        VARCHAR2(50),
  CREATE_DATE      DATE,
  UPDATE_DATE      DATE
)

ALTER TABLE USER_TEST ADD CONSTRAINT PK_USER_ID PRIMARY KEY (ID)
然后插入一条测试数据:
insert into user_test values('A43G7KHHML903ADF','gameloft9','123456','vivian',sysdate,sysdate)
然后开始编写model、dao和mapper文件:

model

package com.xxx.demo.dataaccess.model;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * Created by gameloft9 on 2017/11/28.
 */
@Data
public class UserTest implements Serializable{
    private static final long serialVersionUID = 1L;
    /**id*/
    private String id;
    /**登录名*/
    private String loginName;
    /**密码*/
    private String password;
    /**真实姓名*/
    private String realName;
    /**创建时间*/
    private Date createTime;
    /**更新时间*/
    private Date updateTime;
}

dao

package com.xxx.demo.dataaccess.dao;

import org.apache.ibatis.annotations.Param;

/**
 * Created by gameloft9 on 2017/11/28.
 */
public interface UserMapper {

    /**
     * 查找用户
     * @param loginName 登录名
     * @param password 密码(为了不增加复杂度,这里不进行加密,使用明文)
     * */
    Integer countUserByNameAndPwd(@Param("loginName") String loginName,
                                  @Param("password") String password);
}

mapper

<?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映射-->
<mapper namespace="com.xxx.demo.dataaccess.dao.UserMapper" >
  <!--结果集映射-->
  <resultMap id="BaseResultMap" type="com.xxx.demo.dataaccess.model.UserTest" >
    <id column="ID" property="id" jdbcType="VARCHAR" />
    <result column="LOGIN_NAME" property="loginName" jdbcType="VARCHAR" />
    <result column="PASSWORD" property="password" jdbcType="VARCHAR" />
    <result column="REAL_NAME" property="realName" jdbcType="VARCHAR" />
    <result column="CREATE_TIME" property="createTime" jdbcType="TIMESTAMP" />
    <result column="UPDATE_TIME" property="createTime" jdbcType="TIMESTAMP" />
  </resultMap>

  <sql id="Base_Column_List" >
    ID,LOGIN_NAME,PASSWORD,REAL_NAME,CREATE_TIME,UPDATE_TIME
  </sql>

  <select id="countUserByNameAndPwd" resultType="Integer" parameterType="map" >
    <!--
     自己编写SQL,resultType是返回的字段类型,parameterType是请求的参数类型。
     loginName,password对应于mapper里面的参数名称
    -->
   select count(1) from USER_TEST t WHERE
    t.LOGIN_NAME = #{loginName,jdbcType=VARCHAR}
    AND t.PASSWORD = #{password,jdbcType=VARCHAR}
  </select>
 
</mapper>

然后定义好接口和实现类:

UserService接口

package com.xxx.demo.dataaccess.api;

/**
 * Created by gameloft9 on 2017/11/28.
 */
public interface UserService {

    /**
     * 验证用户
     * @param loginName 登录名
     * @param password 密码(为了不增加复杂度,这里不进行加密,使用明文)
     * */
    Boolean validateUser(String loginName,String password);
}

UserServiceImpl

package com.xxx.demo.dataaccess.impl;

import com.xxx.demo.dataaccess.api.UserService;
import com.xxx.demo.dataaccess.dao.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by gameloft9 on 2017/11/28.
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper dao;

    /**
     * 验证用户
     * @param loginName 登录名
     * @param password 密码(为了不增加复杂度,这里不进行加密,使用明文)
     * */
    public Boolean validateUser(String loginName,String password){
        int n = dao.countUserByNameAndPwd(loginName,password);
        return n>0?true:false;
    }
}


整个结构层次如图所示:

然后修改controller中的验证代码如下:
package com.xxx.demo.controllers;

import com.xxx.demo.dataaccess.api.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;

/**
 * Created by gameloft9 on 2017/11/27.
 */
@Slf4j
@Controller
public class LoginController {

   @Autowired
    UserService userServiceImpl;

    /**
     * 登录页面入口
     */
    @RequestMapping(value = "/index.do", method = RequestMethod.GET)
    public String index(Model model, HttpServletResponse response) {
        log.info("进入登录页面");
        return "login";
    }

    /**
     * 登录请求
     */
    @RequestMapping(value = "/login.do", method = RequestMethod.POST)
    public String login(Model model, String name, String pwd) {
        if(StringUtils.isBlank(name)||StringUtils.isBlank(pwd)){
           log.info("用户名和密码为空");
            model.addAttribute("errInfo", "用户名或密码不能为空!");
            return "login";
        }

        Boolean passed = userServiceImpl.validateUser(name,pwd);
        if(passed){
            log.info("验证成功!");
            model.addAttribute("name", name);
            return "home";
        }

        log.info("用户名密码错误,name:{},pwd:{}",name,pwd);
        model.addAttribute("errInfo", "用户名或密码错误!");
        return "login";
    }
}

三、运行结果

1、输入错误账号密码

2、输入正确账号密码


3、日志


如何打印SQL日志?

        我们发现,日志里面并没有SQL日志,有时候我们需要查询SQL日志及其参数是否是正确的,这个时候mybatis-config.xml文件就派上用场了。我们可以添加mybatis插件来进行日志打印,比在logback定义logger的好处是,我们可以控制是否打印SQL日志,这对于避免日志过大是很有帮助的。

配置插件

<?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>
<!--可以在这里配置插件,比如打印SQL-->
    <plugins>
        <plugin interceptor="com.xxx.demo.dataaccess.interceptor.DialectStatementHandlerInterceptor">
            <property name="debug" value="true"/>
        </plugin>
    </plugins>
</configuration>

编写插件

package com.xxx.demo.dataaccess.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

/**
 * SQL拦截器,控制日志打印
 * @author gameloft9
 * 2017-11-29
 */
@Slf4j
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class DialectStatementHandlerInterceptor implements Interceptor {

	/**是否开启debug模式*/
	private String debug;

	public Object intercept(Invocation invocation) throws Throwable {
		RoutingStatementHandler statement = (RoutingStatementHandler) invocation
				.getTarget();
		if ("true".equals(debug)) {
			log.info("Executing SQL: {}", statement.getBoundSql().getSql().replaceAll("\\s+", " "));
			log.info("\twith params: {}", statement.getBoundSql().getParameterObject());
		}
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
		this.debug = (String) properties.getProperty("debug");
	}
}

运行结果



源代码:

github工程地址:https://github.com/gameloft9/spring-mybatis-demo
csdn源代码地址:http://download.csdn.net/download/gameloft9/10137694

下一篇将集成shiro权限控制,敬请期待。

猜你喜欢

转载自blog.csdn.net/gameloft9/article/details/78652740