spring+mybatis详细整合

本文是作者原创文章,如需转载,请先联系

这几天一直都在做spring+mybaits的整合项目,但是其中遇到很多问题。所以现在开始重新学习一下spring,从最简单的开始,下面演示一个demo。

工程目录

本次demo的工程目录如下:


工程目录图

注解:其中涂了红色的请忽略,是做其他作用的。包名为:justspring,下面有两个包分别是dao和service。dao用于模拟以后的数据库操作,service,顾名思义是服务层。
这两个包当中各有两个文件,一个是接口(interface)一个是接口的实现(interfaceImpl)。

详细代码(version 1.0)

dao(数据持久层)

//dao.java 文件名(下同)

package justspring.dao;

public interface Dao {
    String sayHello();
}
//daoImpl.java

package justspring.dao;

public class DaoImpl implements Dao {
    @Override
    public String sayHello() {
        return "hello by dao";
    }
}

以上是dao层的代码,因为这是一个demo,所以每个文件只有一个方法用于示范。在真实的环境当中,dao层应该是和数据库打交道的,dao只提供接口而具体的实现可以通过mybatis的sql映射来实现,或者自己编写数据库的操作语句。在这里,因为是demo所以采用了后者,并且没有写数据库操作语句。

service(服务层)

//service.java 

package justspring.service;

public interface Service {
    String sayHello();
    String otherService();
}
//serviceImpl.java

package justspring.service;

import justspring.dao.Dao;

public class ServiceImpl implements Service {
    private Dao dao;
    public void setDao(Dao dao) {
        this.dao = dao;
    }
    @Override
    public String sayHello() {
        return dao.sayHello();
    }
    @Override
    public String otherService() {
        return "other service";
    }
}

服务,是用来承接控制层和数据层的,数据层取得的数据要在这里进行各种逻辑操作。将service细分为两小层:

  • service 服务接口层
  • serviceImpl 服务实现层

服务接口层很简单,就是将提供的服务逐一的列出来,不管具体的实现。服务实现层,则是负责实现每一个服务,这样有利于后期的维护和升级。一般来说,我们从数据库查到的信息都是要拿来进行各种逻辑操作的,这个操作的场所正是服务实现层。所以,等下会在服务实现层里面由spring注入dao接口。既然如此为什么服务实现层不直接实现dao接口就好了呢?因为,真正的系统里面还会有一些不依靠数据库或者不同数据库的操作,所以其他服务需要在服务接口层进行声明。

在这里,最重要的就是这个serviceImpl了。它负责实现服务,我们可以看到这里面有一个Dao的对象,但是奇怪的是我们并没有给它new任何的实例。如果你对spring有一点了解的话就会知道,这里将会使用spring的依赖注入(Ioc)来实现了。后面的spring的配置文件里会有说明。

这里有两个方法,其中的sayHello方法是使用了dao的方法,otherService则可以想象成是一些不需要数据库的服务。

spring配置文件

相信熟悉spring的都会知道,spring需要有一个配置文件,主要就是用来配置bean的。所以,这个文件很重要,随着spring的发展,越来越多简便的配置方法也出来了,很容易把人搞晕,我这次重新研究的重点就是想找到最简便最清晰的配置方法。但是,我还没研究出来,所以这里采用最原始的配置方法来演示。

//spring1.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

       <!--创建一个一个名字(可以用name或者id来表示,唯一标识,不可重复)为dao的bean,class表示它的类(必须是类,不可以是接口或者抽象类)-->
       <!--这个主要是用来等着被下面的bean使用的-->
       <bean id="dao" class="justspring.dao.DaoImpl"/>

       <!--创建一个名字为service,类型是ServiceImpl的bean,并且将这个bean注入到ServiceImpl.java的成员变量dao当中-->
       <!--property 当中的name是成员变量的名字(需要保持一致),ref是指注入的是上面创建的bean-->
       <bean id="service" class="justspring.service.ServiceImpl">
              <property name="dao" ref="dao"/>
       </bean>
</beans>

测试

做到这里就基本是万事具备了,我们来测试一下是否真的成功了。为了不写main函数那套这里用了Junit4来进行,需要在maven下添加一下依赖包。

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>testJustSpring</scope>
</dependency>
import justspring.service.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testJustSpring {

    @Test
    public void test() {
        //引入spring的配置文件获取上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
        //通过上下文和bean的名字获取service实例,就是刚才在配置文件下配置的第二个
        //其实这里也可以通过依赖注入来实现,不过需要写setter代码不太好看        
        Service service = (Service) context.getBean("service");
        String string = service.sayHello();
        System.out.println(string);
    }
}

通过上面的代码,我们可以得到的结果如下:

hello by dao

简化spring配置过程

通过上面的配置,成功地得到了想要的结果。但是,如果bean很多的时候,我们都需要逐一的去配置就会显得非常的麻烦。spring团队也一直致力于简化.xml的配置步骤,这里简述一下如何使用注解来简化配置。

要想使用注解进行xml配置,需要在xml当中添加以下代码:

<context:annotation-config/>
<context:component-scan base-package="justspring.service,justspring.dao"/>

以上第一行是开启注解,第二行是扫描注解包。如果有第二行代码,第一行可以省略。

对于不同的层,spring提供了不同的注解。详细的情况如下所示:

  • dao层使用 @Repository,可以在其后添加("")来指定bean的名字(即name或者id)
  • service层使用 @Service,可以在其后添加("")来指定bean的名字(即name或者id)
  • controller层使用 @Controller,可以在其后添加("")来指定bean的名字(即name或者id)
  • 其他情况下可以使用 @Component,可以在其后添加("")来指定bean的名字(即name或者id)

注意,以上注解都是在接口的实现类进行。并且,如果没有指定名字,系统会默认起名,规则是:取类名,将第一个字母转换为小写。

使用以上的方法,我们可以完成将bean在xml的注册。但是,如果在某一类当中,我们需要使用bean时,怎么将xml当中的bean注入到类当中呢?这里要涉及到bean的自动装配策略。相关的注释主要有@Resource和@Autowired。这里不详细讲述他们的区别,需要看详细介绍了可以看这篇文章,本文使用@Resource进行。回到代码当中,将上面的代码做出如下的修改。

//DaoImpl.java

package justspring.dao;

import org.springframework.stereotype.Repository;

@Repository("dao") //添加了这行代码
public class DaoImpl implements Dao {
    @Override
    public String sayHello() {
        return "hello by dao";
    }
}
//ServiceImpl.java

package justspring.service;

import justspring.dao.Dao;
import javax.annotation.Resource;

//因为这个工程里有Service类,为了不发生歧义,使用包名+类名
@org.springframework.stereotype.Service("service")
public class ServiceImpl implements Service {

    @Resource //添加了这个注释,将上面的dao注入到这里
    private Dao dao;

    @Override
    public String sayHello() {
        return dao.sayHello();
    }

    @Override
    public String otherService() {
        return "other service";
    }
}

有了上面两个代码文件,我们的xml就解放出来了。不需要再进行任何的bean注册了,现在的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">

       <context:annotation-config/>
       <context:component-scan base-package="justspring.service,justspring.dao"/>
</beans>

xml文件一下子干净了很多,省掉了配置的麻烦。测试类的代码不作任何的变化,运行后可以得到与之前一样的结果。测试类当中的service还是要通过getBean来实现的,我尝试着用@Resource像注入dao那样的方法来做,但是并没有能够成功。报错空指针,这里接下来还需要认真的研究一下。

详细代码(version 2.0)

前面已经得到了一个完整的spring工程,但是dao部分关于数据库的还没有实现,下面来完成怎么连接数据库,并且从数据库里面获取到数据的操作。首先需要在maven下添加需要的依赖包,这里不一一列举了,可以参看我这个系列的第一篇文章

Mybatis-Spring

Mybatis,是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

Mybatis有一个中文的说明文档,需要可以点击这里查看

由于我们是在spring的框架下使用mybait,所以除了配置mybatis外,还需要将它和spring框架结合在一起。方便的是,已经存在无缝连接它们的库了,那就是Mybaits-Spring。

Mybatis-Spring有一个中文的说明文档,需要可以点击这里查看

注意,中文版不够详细,有能力还是请阅读英文版

通过这个文档,总结一下使用的方法。

安装

使用整个模块前需要引入必要的jar包,或者添加依赖文件.
如果使用的是maven,在pom.xml当中添加如下:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>x.x.x</version>
</dependency>

开始

要和spring一起使用mybatis,需要在spring的上下文当中定义至少两样东西。分别是:

  • 一个SqlSessionFactory
  • 至少一个数据映射器类

配置SqlSessionFactory

其中,第一点很容易配置,先把它干掉。直接在spring的上下文配置就可以了,注意,整合后,不需要单独的mybatis配置文件,全部的配置内容都可以在spring的上下文当中进行。

//spring1.xml 在上面已有的基础上添加

//数据源配置,需要根据个人情况填写
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/数据库名" />
  <property name="username" value="用户名" />
  <property name="password" value="密码" />
</bean>

//SqlSessionFactory配置,注意此处有大坑(如果你使用Intellij这个ide的话请看后面的填坑指南)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

注意:上面的的文件不能照着填,需要将路径理清楚,如果你自己在尝试着写的话,请把路径换成你自己的。这里使用的是mysql数据库,如果不是请按照实际情况填写。Intellij编译工程的时候,会按照目录的种类进行编译,xml文件在src的源码目录下的话不会编译到target里面,需要单独进行设置。

配置数据映射器类

配置数据映射器的方法有很多,先简要的叙述一下:

官方说明文档在这里(这里不要去看中文的翻译版,不单止内容缺少,而且讲得非常不清楚)

总结一下来说就是两种方法:

  • 利用xml来进行显示的逐一配置(如果mapper很多的话就会很麻烦)
  • 利用mybatis-spring提供的自动扫描机制(就像spring当中扫描dao和service组件那样)

有简单的方法使用,相信没有人希望用难的方法,所以这里使用上述的第二种方法进行,在spring1.xml(即spring的配置文件)当中添加几句代码可以:

//spring1.xml 现在我的spring1.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:mybatis="http://mybatis.org/schema/mybatis-spring"
       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
       http://mybatis.org/schema/mybatis-spring
       http://mybatis.org/schema/mybatis-spring.xsd">

        //添加了这一句和多引用了一个命名空间,请和上面的文件对比着看
        <mybatis:scan base-package="justspring.dao" />

        ...省略相同的代码

</beans>

添加数据库查询操作以及包装服务层代码

配置方面现在就都做好了,需要添加一些数据库的操作代码(写在xml配置文件上),另外在dao和service提供一些接口并实现它。在dao真正操作数据库的时候不需要我们自己写实现类,按照上面的配置,保证xml的名字和接口类的名字对应一致,框架会替我们做好实现。要实现操作数据库,首先要有数据表,我们这里建了一个用来测试,如下:

括号表示存在表里的数据

表名:person

字段1:LOG_NM("管理员0")

字段2:LOG_PW("123456")

//Dao.java

package justspring.dao;

import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository //没有实现类DaoImpl,所以将这个注解放在了这里
public interface Dao {
//    @Select("SELECT LOG_PW FROM person WHERE LOG_NM = #{name}")
//通过@Select注释也可以不用写xml直接进行数据库操作,但是在比较复杂的查询情况下还是需要xml的(官方的说法),所以这里还是使用xml的方法
    String getPwByUserNm(@Param("name")String name);
//@Param是一个注解,表示传进去的参数名字,在xml当中通过@Param括号里面的名字,可以获取到传进去的参数
}
//Service.java

package justspring.service;

public interface Service {
    String getPwByUserNm(String name);
}
//ServciceImpl.java

package justspring.service;

import justspring.dao.Dao;
import org.apache.ibatis.annotations.Param;

import javax.annotation.Resource;

@org.springframework.stereotype.Service("service")
public class ServiceImpl implements Service {

    //同上,注入一个dao
    @Resource
    private Dao dao;

    //实现通过用户名获取密码的服务
    @Override
    public String getPwByUserNm(String name) {
        return this.dao.getPwByUserNm(name);
    }
}

//Dao.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="justspring.dao.Dao">
    <select id="getPwByUserNm" resultType="java.lang.String">
        SELECT LOG_PW FROM person WHERE LOG_NM = #{name}
    </select>
</mapper>
//testJustSpring.java

package justspring.test;

import justspring.service.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.annotation.Resource;

public class testJustSpring {

    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
        Service service = (Service) context.getBean("service");
        String name = "管理员0";
        String password = service.getPwByUserNm(name);
        System.out.println(password);
    }
}

以上,代码已经完全展示出来。但是前面说的大坑还没有解决,为解决它先看一下正确的文件结构应该这样设置:

新文件结构

请将文件结构调整如上!

请将文件结构调整如上!

请将文件结构调整如上!

重要的事情说三遍,test的包可以不调整,没有影响。并且点击工程根目录,按下F12打开模块设置,将resources目录设置为resources root类型,点击apply。重新编译一次,应该就能够在.targe下看到Dao.xml文件了。详情请看这里,非常感谢解决方案。

执行完这步操作,运行一下就应该可以看到:

123456

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容