springBoot(五):SpringBoot中MyBatis

mybatis是一個比較好的orm框架,至少我比較喜歡使用 ,對比hibernate稍微複雜了一些,但是這種複雜讓我們可以靈活的編寫sql,是我們的開發靈活性大大增加,但是這樣必須對sql必須特別熟悉,包括一些sql優化;只有這樣我們才能做出性能給常出色的系統。

我們知道在spring MVC中使用mybatis,是比複雜的;我配置各種各樣的配置文件,並且在mapper文件中還要逐個聲明對象的映射關係。那麼mybatis在springBoot會不會更簡單一些呢,接下來我們一起來看一下吧:

mybatis-spring-boot-starter、

官方說明:MyBatis Spring-Boot-Starter will help you use MyBatis with Spring Boot
這是mubatis基於springBoot開發的一套解決方案,目的是在springBoot中更加簡單的使用,mybatis-spring-boot-starter主要有兩種解決方案,一種是使用註解解決一切問題,一種是簡化後的老傳統。

首先我們引入mybatis-spring-boot-starter的pom依賴

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

首先說我們使用最新版本的1.3.2版本,另外引入mybatis需要寫上版本號(沒辦法,誰讓咱們不是親生的呢!!!)


下面我們來分別介紹兩種開發模式

無配置文件註解版

那就讓註解來搞定一切吧!

1. 完整的pom.xml文件

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<optional>true</optional>
		<scope>true</scope>
	</dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-jdbc</artifactId>
       </dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
	</dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>1.3.2</version>
	</dependency>
</dependencies>

這是我完整的配置文件,可能有些其他的jar包,請忽略!!!

2. application.yml 添加相關配置

mybatis:
  type-aliases-package: com.neo.entity
spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8
    username: root
    password: admin

springBoot會自動加載這些配置,數據源會注入到sqlSessionFactory中,sqlSessionFactory會自動注入到Mapper中,我們只需要使用它就行啦。

在啓動類中添加對mapper包掃描@MapperScan

@SpringBootApplication
@MapperScan("com.neo.mapper")
public class Application {

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

或者每個Mapper類上都添加@Mapper註解,兩種方式,任選一種就行。

3. 開發Mapper

這是最總要的一步,我們會在Mapper接口中使用註解的方式寫sql語句

public interface UserMapper {

    @Select("SELECT * FROM users")
    @Results(id = "resultMap", value = {
            @Result(property = "sex",  column = "sex", javaType = SexEnum.class),
            @Result(property = "userName", column = "user_name"),
            @Result(property = "userPwd", column = "user_pwd")
    })
    List<User> getAll();

    @Select("SELECT * FROM users WHERE id = #{id}")
    @ResultMap("resultMap")
    User getOne(Long id);

    @Insert("INSERT INTO users(name, age, sex, user_name, user_pwd) VALUES (#{name}, #{age}, #{sex}, #{userName}, #{userPwd})")
    void insert(User user);

    @Update("UPDATE users SET name=#{name}, user_name=#{userName} WHERE id =#{id}")
    void update(User user);

    @Delete("DELETE FROM users WHERE id = #{id}")
    void delete(Long id);
}

爲了更接近生產我特地將user_name、user_pwd兩個屬性在數據庫加了下劃線和實體類屬性名不一致,另外sex使用了枚舉

  • @Select 是查詢類的註解,所有的查詢均使用這個
  • @Result 修飾返回的結果集,關聯實體類屬性和數據庫字段一一對應,如果實體類屬性和數據庫屬性名保持一致,就不需要這個屬性來修飾。
  • @Insert 插入數據庫使用,直接傳入實體類會自動解析屬性到對應的值
  • @Update 負責修改,也可以直接傳入對象
  • @delete 負責刪除

如果想了解詳細的使用方式,請去看官方文檔

上面我們只使用可#{value}取值,請不要誤解${value}的用法:

// 使用#{value}直接將類型注入進去,字符串兩邊不需要寫單引號
@Select("Select * from teacher where name = #{name}")
Teacher query(@Param("name") String name);

// 而使用$是將字符串取出來,類型需要自己掌握
@Select("Select * from teacher where name = '${name}'")
Teacher query(@Param("name") String name);
@RestController
public class MyController {

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("query")
    public List<User> query(){
        return userMapper.getAll();
    }

    @RequestMapping("save")
    public void save(){
        UserMapper.insert(new User("aa", 12, SexEnum.GIRL, "123456", "123456");
        UserMapper.insert(new User("bb", 13, SexEnum.GIRL, "123456", "123456");
        UserMapper.insert(new User("cc", 15, SexEnum.BOY, "123456", "123456");
    }
}

下面會講一些註解的詳細用法,接下來我們先看使用mapper文件怎麼使用吧:

簡單Mapper文件開發

這種開放方式保留了mybatis-config.xml文件,和mapper.xml文件,不需要寫dao類,直接映射到mapper接口中的方法;也就是說上面的註解的部分寫到了mapper文件中。

1. 配置

pom.xml文件和之前完全相同。

application.yml如下:

mybatis:
  type-aliases-package: com.neo.entity
  # 添加配置文件和mapper文件的位置
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8
    username: root
    password: admin

mybatis-config.xml 配置

<configuration>
	<typeAliases>
		<typeAlias alias="Integer" type="java.lang.Integer" />
		<typeAlias alias="Long" type="java.lang.Long" />
		<typeAlias alias="HashMap" type="java.util.HashMap" />
		<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
		<typeAlias alias="ArrayList" type="java.util.ArrayList" />
		<typeAlias alias="LinkedList" type="java.util.LinkedList" />
	</typeAliases>
</configuration>

這個文件和以前在springMvc中使用的全局配置文件相同,其他配置項,自己去看官方文檔。

2、添加User的映射文件

<mapper namespace="com.neo.mapper.UserMapper" >
    <resultMap id="BaseResultMap" type="com.neo.entity.UserEntity" >
        <id column="id" property="id" jdbcType="BIGINT" />
        <result column="userName" property="userName" jdbcType="VARCHAR" />
        <result column="passWord" property="passWord" jdbcType="VARCHAR" />
        <result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>
        <result column="nick_name" property="nickName" jdbcType="VARCHAR" />
    </resultMap>
    
    <sql id="Base_Column_List" >
        id, userName, passWord, user_sex, nick_name
    </sql>

    <select id="getAll" resultMap="BaseResultMap"  >
       SELECT 
       <include refid="Base_Column_List" />
	   FROM users
    </select>

    <select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >
        SELECT 
       <include refid="Base_Column_List" />
	   FROM users
	   WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="com.neo.entity.UserEntity" >
       INSERT INTO 
       		users
       		(userName,passWord,user_sex) 
       	VALUES
       		(#{userName}, #{passWord}, #{userSex})
    </insert>
    
    <update id="update" parameterType="com.neo.entity.UserEntity" >
       UPDATE 
       		users 
       SET 
       	<if test="userName != null">userName = #{userName},</if>
       	<if test="passWord != null">passWord = #{passWord},</if>
       	nick_name = #{nickName}
       WHERE 
       		id = #{id}
    </update>
    
    <delete id="delete" parameterType="java.lang.Long" >
       DELETE FROM
       		 users 
       WHERE 
       		 id =#{id}
    </delete>
</mapper>

其實就是將剛纔的註解全部寫到xml中。

3、編寫Mapper接口

public interface UserMapper {
	
	List<UserEntity> getAll();
	
	UserEntity getOne(Long id);

	void insert(UserEntity user);

	void update(UserEntity user);

	void delete(Long id);

}

注意啦!這裏的mapper接口中的方法名稱要和mapper.xml文件中標籤的id屬性完全對應起來。

4、使用

測試就和剛纔的Controller中的代碼相同

如何選擇

兩種模式各有特點,註解版適合簡單快速開發,當多表連接查詢的需求非常少時,越適合這種模式。

老的mapper.xml文件的方式適合大項目,可以靈活的書寫sql,方便調整SQL,寫起sql更瀟灑;

那麼不使用mapper.xml文件真的不能完成複雜語句查詢嗎?答案肯定是否定的,還是有解決方案的:

解決方案一

我們可以直接在註解中使用 <script> 標籤;如下:

@Select({
    "<script>",
        "SELECT ",
        "p.product_id, p.name, p.value, p.type, p.create_date, u.user_id, u.name ",
        "FROM product p",
        "LEFT JOIN user u ON u.user_id=p.create_user",
        "<where> ",
        "<if test='name != null'>",
            "and name=#{product.name}",
        "</if> ",
        "<if test='value != null'>",
            "and value=#{product.value}",
        "</if> ",
        "</where> ",
    "<script>",
})
@ResultMap("resultMap")
List<Product> findListByUser(@Param("product") Product user);

當我們使用 <script> 標籤將註解中的sql語句包圍住以後,被包圍的sql語句就可以使用和mapper.xml 中相同的語法了

這樣是暫時解決了複雜的 mapper.xml 文件問題,但是這樣寫代碼可讀行下降了很多,可以說比 mapper.xml 文件還要糟糕

解決方案二

第一種方案中,sql語句的可讀性變得很差;這裏我們使用 mybatis@SelectProvider 註解,實現在方法中拼接sql,如下:

public interface UserMapper {

    @SelectProvider(type=UserMapper.class, method="createSql")
    @ResultMap("resultMap")
    List<Product> findListByUser(@Param("product") Product user);

    class UserProvider {
        public String createSql(Map<String, Product> param) {
            StringBuffer sql = new StringBuffer();
            sql.append("SELECT p.product_id, p.name, p.value, p.type, ");
            sql.append("p.create_date, u.user_id, u.name ");
            sql.append("FROM product p");
            sql.append("LEFT JOIN user u ON u.user_id=p.create_user");
            sql.append("WHERE 1=1 ");
            if(!StringUtils.isEmpty(param.get("product").getName())) {
                sql.append("and name=" + param.get("product").getName());
            }
            if(!StringUtils.isEmpty(param.get("product").getValue())) {
                sql.append("and value=" + param.get("product").getValue());
            }
            return sql.toString();
        }
    }
}

這裏就簡單展示一下,這裏需要注意的事項有,這裏不詳細介紹:

  • @Param 註解的使用
  • 構造sql語句函數的參數問題
  • #{name} 這類參數參數的使用

注意,Provider註解有多種註解,包括 SelectProviderInsertProviderUpdateProviderDeleteProvider,分別代表增刪改查操作,這裏也不詳細介紹。

解決方案三

上面那一種解決方案,雖然解決了大部分代碼可讀性,但是 sql 拼接起來還是比較複雜,可讀性並沒有達到最好

這裏我們使用 mybatis 自帶的 SQL 類,進行拼接sql,我們在修改上面的那一部分代碼:

public interface UserMapper {

    @SelectProvider(type=UserMapper.class, method="createSql")
    @ResultMap("resultMap")
    List<Product> findListByUser(@Param("product") Product user);

    class UserProvider {

        public String createSql(Map<String, Product> param) {
            return  new SQL() {{
                SELECT("p.product_id, p.name, p.value, p.type, ");
                SELECT("p.create_date, u.user_id, u.name ");
                FROM("product p");
                LEFT_OUTER_JOIN("user u ON u.user_id=p.create_user")
                sql.append("WHERE 1=1 ");
                if(!StringUtils.isEmpty(param.get("product").getName())) {
                    WHERE("name=#{product.name}");
                }
                if(!StringUtils.isEmpty(param.get("product").getValue())) {
                    WHERE("and value=#{product.value}");
                }
            }}.toString();
        }
    }
}

SQL 這個類只是爲我們提供了一個簡單的sql拼裝方法,具體使用方式,自己探索吧,不過不推薦上面的這種寫法,因爲雙大括號,會讓編譯後的文件多處一個.class 文件,如果這樣的操作過多,會是項目運行變慢哦,正常的寫法自己探索吧,因爲 SQL 這個類實現方式很簡單,看一下源碼,就懂了。

這裏提一下:在mybatis之前的版本中,有使用 SqlBuilder,但是現在這個方法已經過時了,不再推薦使用;但是有一點比較坑的地方是,如果我們看 SqlBuilder的源碼,會發現,官方只標記它過時的類,但是沒有指出更好的實現方式;這個自己找還是比較難找的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章