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
註解有多種註解,包括 SelectProvider
、InsertProvider
、UpdateProvider
、DeleteProvider
,分別代表增刪改查操作,這裏也不詳細介紹。
解決方案三
上面那一種解決方案,雖然解決了大部分代碼可讀性,但是 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
的源碼,會發現,官方只標記它過時的類,但是沒有指出更好的實現方式;這個自己找還是比較難找的。