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
的源码,会发现,官方只标记它过时的类,但是没有指出更好的实现方式;这个自己找还是比较难找的。