一、前言

一個現實的場景是:當我們開發一個Web工程時,架構師和開發工程師可能更關心項目技術結構上的設計。而幾乎所有結構良好的軟件(項目)都使用了分層設計。分層設計是將項目按技術職能分爲幾個內聚的部分,從而將技術或接口的實現細節隱藏起來。

從另一個角度上來看,結構上的分層往往也能促進了技術人員的分工,可以使開發人員更專注於某一層業務與功能的實現,比如前端工程師只關心頁面的展示與交互效果(例如專注於HTML,JS等),而後端工程師只關心數據和業務邏輯的處理(專注於Java,Mysql等)。兩者之間通過標準接口(協議)進行溝通。

在實現分層的過程中我們會使用一些框架,例如SpringMVC。但利用框架帶來了一些使用方面的問題。我們經常要做的工作就是配置各種XML文件,然後還需要搭建配置Tomcat或者Jetty作爲容器來運行這個工程。每次構建一個新項目,都要經歷這個流程。更爲不幸的是有時候前端人員爲了能在本地調試或測試程序,也需要先配置這些環境,或者需要後端人員先實現一些服務功能。這就和剛纔提到的“良好的分層結構”相沖突。

每一種技術和框架都有一定的學習曲線。開發人員需要了解具體細節,才知道如何把項目整合成一個完整的解決方案。事實上,一個整合良好的項目框架不僅僅能實現技術、業務的分離,還應該關注並滿足開發人員的“隔離”。

爲了解決此類問題,便產生了Spring Boot這一全新框架。Spring Boot就是用來簡化Spring應用的搭建以及開發過程。該框架致力於實現免XML配置,提供便捷,獨立的運行環境,實現“一鍵運行”滿足快速應用開發的需求。

與此同時,一個完整的Web應用難免少不了數據庫的支持。利用JDBC的API需要編寫複雜重複且冗餘的代碼。而使用O/RM(例如Hibernate)工具需要基於一些假設和規則,例如最普遍的一個假設就是數據庫被恰當的規範了。          這些規範在現實項目中並非能完美實現。由此,誕生了一種混合型解決方案——Mybatis。 Mybatis是一個持久層框架 它從各種數據庫訪問工具中汲取大量優秀的思想,適用於任何大小和用途的數據庫。根據官方文檔的描述:MyBatis 是支持定製化 SQL、存儲過程以及高級映射的優秀的持久層框架。M yBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或註解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

最後,再回到技術結構分層上,目前主流倡導的設計模式爲MVC,即模型(model)-視圖(view)-控制器(controller)。實現該設計模式的框架有很多,例如Struts。而前文提到的SpringMVC是另一個更爲優秀,靈活易用的MVC框架。 SpringMVC是一種基於Java的以請求爲驅動類型的輕量級Web框架,其目的是將Web層進行解耦,即使用“請求-響應”模型,從工程結構上實現良好的分層,區分職責,簡化Web開發。

目前,對於如何把這些技術整合起來形成一個完整的解決方案,並沒有相關的最佳實踐。將Spring Boot和Mybatis兩者整合使用的資料和案例較少。因此,本文提供(介紹)一個完整利用SpringBoot和Mybatis來構架Web項目的案例。該案例基於SpringMVC架構提供完整且簡潔的實現Demo,便於開發人員根據不同需求和業務進行拓展。

補充提示,Spring Boot 推薦採用基於 Java 註解的配置方式,而不是傳統的 XML。只需要在主配置 Java 類上添加“@EnableAutoConfiguration”註解就可以啓用自動配置。 Spring Boot 的自動配置功能是沒有侵入性的,只是作爲一種基本的默認實現。開發人員可以通過定義其他 bean 來替代自動配置所提供的功能,例如在配置本案例數據源(DataSource)時,可以體會到該用法。

=====================

首先分析一下各個框架的職責:

SpringMVC:負責表現層

Service接口:處理業務

Mapper:持久層

spring負責將各層之間整合

通過Spring管理持久層的mapper(相當於Dao接口)

通過Spring管理業務層的service,service中可以調用mapper接口

Spring進行事務控制

通過Spring管理表現層handler,handler中可以調用service接口

mapper、service、handler都屬於javabean

二、集成MyBatis

Spring Boot 集成MyBatis有兩種方式,一種簡單的方式就是使用MyBatis官方提供的:

Mybatis 官方提供了 mybatis-spring-boot-starter

https://github.com/mybatis/spring-boot-starter

http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

另外一種方式就是仍然用類似 mybatis-spring 的配置方式,這種方式需要自己寫一些代碼,但是可以很方便的控制MyBatis的各項配置。

mybatis有很多優點。

  1. 易於上手和掌握。
  2. sql寫在xml裏,便於統一管理和優化。
  3. 解除sql與程序代碼的耦合。
  4. 提供映射標籤,支持對象與數據庫的orm字段關係映射
  5. 提供對象關係映射標籤,支持對象關係組建維護
  6. 提供xml標籤,支持編寫動態sql。

缺點:筆者自己總結了下(建議使用註解和sql 構建器來寫mybatis ,如果使用xml 就會有一些麻煩事了。)

  1. 其實在開發過程中還是有一些不方便的地方以下列成幾種。大多數人習慣於xml 形式。spring loader 不支持熱部署xml 的。如果寫入sql 構建器。就是通過java 代碼來構建sql 又很多人不是特別瞭解。所以,你改一次就要重啓。就有點麻煩了。(筆者最不喜歡就是幹浪費時間的事了。)
  2. 就是mybatis 每次寫一個實體的查詢語句。就要建立一個mapper 和xml 進行映射。這樣Mapper越來越多和xml 越來越多。感覺不好管理,= = !。

三、mybatis-spring-boot-starter方式

1、在 pom.xml 中添加依賴:

需要導入  mybatis-spring-boot-starter 

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

MySQL:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

mybatis-spring-boot-starter 依賴樹如下: 

其中 mybatis 使用的3.3.0版本,可以通過: 

<mybatis.version>3.3.0</mybatis.version> 屬性修改默認版本。 

mybatis-spring 使用版本1.2.3,可以通過: 

<mybatis-spring.version>1.2.3</mybatis-spring.version> 修改默認版本。

2. 配置文件、application.properties

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc.username = root
jdbc.password = vvvxxx


mybatis.typeAliasesPackage=com.xxx.firstboot.domain  
mybatis.mapperLocations=classpath:mapper/*.xml

說明:

  • mybatis.typeAliasesPackage:指定domain類的基包,即指定其在*Mapper.xml文件中可以使用簡名來代替全類名(看後邊的UserMapper.xml介紹)
  • mybatis.mapperLocations:指定*Mapper.xml的位置

除了上面常見的兩項配置,還有:

  • mybatis.config:mybatis-config.xml配置文件的路徑
  • mybatis.typeHandlersPackage:掃描typeHandlers的包
  • mybatis.checkConfigLocation:檢查配置文件是否存在
  • mybatis.executorType:設置執行模式( SIMPLE, REUSE, BATCH ),默認爲 SIMPLE

3. 代碼

主函數:

使用註解方式annotation形式:

【Application.java】包含main函數,像普通java程序啓動即可

此外,該類中還包含和數據庫相關的DataSource,SqlSeesion配置內容。

注: @MapperScan (   sample.mybatis.mapper ) 表示Mybatis的映射路徑(package路徑)

@SpringBootApplication
@MapperScan("sample.mybatis.mapper")
public class Application implements CommandLineRunner {

    @Autowired
    private CityMapper cityMapper;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this.cityMapper.findByState("CA"));
    }

}

xml方式: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>
    <typeAliases>
        <package name="sample.mybatis.entity"/>
    </typeAliases>
    <mappers>
        <mapper resource="sample/mybatis/mapper/CityMapper.xml"/>
    </mappers>
</configuration>

application.properties

spring.datasource.schema=import.sql
mybatis.config=mybatis-config.xml

定義一個java的實體類:

public class User {
   Integer id;
   String name;
   Integer age;

}
定義一個dao 的mapper接口和對應xml配置文件

說明:該接口中有兩個方法,

  • 一個普通插入:直接用 @Mapper 註解搞定 
  • 一個插入返回主鍵:需要使用xml來搞定
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* Created by Administrator on 2016/9/2.
*/
@Mapper
public interface UserMapper {
    @Select("select * from user where name = #{name}")
    User findUserByName(@Param("name")String name);    
   /**
    * 插入用戶,並將主鍵設置到user中
    * 注意:返回的是數據庫影響條數,即1
    */
    public int insertUserWithBackId(User user);

}
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">


<!-- 指定工作空間,要與接口名相同,源代碼沒有去看,猜測應該是通過"這裏的namespace.下邊方法的id"來定位方法的 -->
<mapper namespace="com.xxx.firstboot.mapper.UserMapper">
    
    <!-- 若不需要自動返回主鍵,將useGeneratedKeys="true" keyProperty="id"去掉即可(當然如果不需要自動返回主鍵,直接用註解即可) -->
    <insert id="insertUserWithBackId" parameterType="User" useGeneratedKeys="true" keyProperty="id" >
       <![CDATA[
       INSERT INTO tb_user 
       (
           username,
           password
       )
       VALUES
       (
           #{username, jdbcType=VARCHAR},
           #{password, jdbcType=VARCHAR}
       )
       ]]>
   </insert>
    
</mapper>

service使用:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;


public class UserService {
	@Autowired
	UserMapper userMapper;

	public String user(){
		User user = userMapper.findUserByName("王");
		return user.getName()+"-----"+user.getAge();
	}
}

四、mybatis-spring方式

這種方式和平常的用法比較接近。需要添加 mybatis 依賴和 mybatis-spring 依賴。

然後創建一個 MyBatisConfig 配置類:

/**
 * MyBatis基礎配置
 *
 * @author liuzh
 * @since 2015-12-19 10:11
 */
@Configuration
@EnableTransactionManagement
public class MyBatisConfig implements TransactionManagementConfigurer {

    @Autowired
    DataSource dataSource;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("tk.mybatis.springboot.model");

        //分頁插件
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);

        //添加插件
        bean.setPlugins(new Interceptor[]{pageHelper});

        //添加XML目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

上面代碼創建了一個 SqlSessionFactory 和一個 SqlSessionTemplate ,爲了支持註解事務,增加了 @EnableTransactionManagement 註解,並且反回了一個 PlatformTransactionManager Bean。

另外應該注意到這個配置中沒有 MapperScannerConfigurer ,如果我們想要掃描MyBatis的Mapper接口,我們就需要配置這個類,這個配置我們需要單獨放到一個類中。

/**
 * MyBatis掃描接口
 * 
 * @author liuzh
 * @since 2015-12-19 14:46
 */
@Configuration
//TODO 注意,由於MapperScannerConfigurer執行的比較早,所以必須有下面的註解
@AutoConfigureAfter(MyBatisConfig.class)
public class MyBatisMapperScannerConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("tk.mybatis.springboot.mapper");
        return mapperScannerConfigurer;
    }

}
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

這個配置一定要注意 @AutoConfigureAfter(MyBatisConfig.class) ,必須有這個配置,否則會有異常。原因就是這個類執行的比較早,由於 sqlSessionFactory 還不存在,後續執行出錯。

做好上面配置以後就可以使用MyBatis了。

關於分頁插件和通用Mapper集成

分頁插件作爲插件的例子在上面代碼中有。

通用Mapper配置實際就是配置 MapperScannerConfigurer 的時候使用 tk.mybatis.spring.mapper.MapperScannerConfigurer 即可,配置屬性使用 Properties

四、mybatis  

註解批量插入:

@Service @Mapper public interface SynonymMapper { @Select("select * from nlp_chinese_synonym where word1 = #{word1}") public List<SynonymEntity> findWord1(@Param("word1")String word1); @InsertProvider(type = SynonymMapperProvider.class, method = "inserList") public int inserList(List<SynonymEntity> synonymEntityList); public static class SynonymMapperProvider { public String inserList(Map<String, List<SynonymEntity>> entity) { List<SynonymEntity> list = entity.get("list"); StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.append("insert into nlp_chinese_synonym (word1,word2,value,ctime) values"); MessageFormat messageFormat = new MessageFormat("(#'{'list[{0}].word1},#'{'list[{0}].word2},#'{'list[{0}].value},#'{'list[{0}].ctime})"); for (int i = 0; i < list.size(); i++) { stringBuilder.append(messageFormat.format(new Integer[]{i})); stringBuilder.append(","); } stringBuilder.setLength(stringBuilder.length() - 1); stringBuilder.append(" ON DUPLICATE KEY UPDATE value =value,ctime=ctime"); return stringBuilder.toString(); } } }
相關文章