浅入浅出 SpringBoot 集成 Mybatis
自从新公司入职开始,有很多新东西可以学,总体感觉 very excited。SpringBoot + Mybatis 就是其中的基础。算算时间,试用期结束的日子快到了。所以写点东西,也算是对这段时间学习的一个小小的交代。纯属个人编程经验总结,如果有什么不严谨的地方,欢迎评论指正 。
照例解释几个概念 [1]
SpringBoot : Spring 是个在教科书里都已经存在 N 多年的东西了。随着 Convension Over Configureation
这个概念被越来越多的开发者接受并使用。Spring 略显臃肿的配置文件让这个 J2EE
元老级的框架,越发显得捉襟见肘。所以, 顺应 MicroServices
的潮流,SpringBoot 应运而生。用来构建基于 Spring 框架的标准化的独立部署应用程序。(“再也 tmd 不用寄人篱下,活在 WebContainer 的屋檐下了”)。
Spring Boot 简化了基于 Spring 的应用开发,你只需要”run”就能创建一个独立的,产品级别的 Spring 应用。 我们为 Spring 平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数 Spring Boot 应用只需要很少的 Spring 配置。
Mybatis : 说白了就是操作数据库的。
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象) 映射成数据库中的记录。
Mybatis - Spring : Mybatis 针对 Spring 的适配版本, 可以用依赖注入的方式调用 SqlSession。
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 使用这个类库中的类, Spring 将会加载必要的 MyBatis 工厂类和 session 类。 这个类库也提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean 中。 而且它也会处理事务, 翻译 MyBatis 的异常到 Spring 的 DataAccessException 异常(数据访问异常,译者注) 中。最终,它并不会依赖于 MyBatis,Spring 或 MyBatis-Spring 来构建应用程序代码。
话不多说 先看看 Demo [2]
源码地址 -> https://github.com/GavinLee1/HelloMybatis.git
项目运行起来之后 浏览器输入 [localhost:8080/swagger-ui.html] 进行访问
本项目,主要实现了以下几个 API :
- CreateUser
- GetUserById
- ListUserByIdList
访问 API 返回的 Response Message 长这个样子:
从开发环境开始讲起 [3]
技术环境 [3.1]
- Framwork: SpringBoot, Mybatis
- IDE: Intellij
- Project Manager: Maven
- Version Control: Git
- Prgrammong Language: Java
用 Intellij 新建一个 空
项目 [3.2]
1.直接新建一个空的 maven peoject
就行了
2.填写 maven 项目的参数
3.给项目取个名字
4.以上几步都成功了的话,初始项目的结构大概是这个样子的
需要添加的 Maven Dependencies [3.3]
SpringBoot 相关的 Dependencies
注意添加 parent 节点
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.8.RELEASE</version> </parent> <properties> <spring-boot.version>1.3.8.RELEASE</spring-boot.version> <spring.context.version>4.2.4.RELEASE</spring.context.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.context.version}</version> </dependency> </dependencies>
Mybatis 相关的 Dependencies
<properties> <mybatis.version>3.4.2</mybatis.version> <mybatis-spring.version>1.3.1</mybatis-spring.version> </properties> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> </dependencies>
数据库连接需要的 Dependencies
<properties> <spring-jdbc.version>4.3.5.RELEASE</spring-jdbc.version> <mysql-connector.version>5.1.38</mysql-connector.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring-jdbc.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector.version}</version> </dependency> </dependencies>
项目结构 [3.4]
根据 MVC 模式的大原则,本项目的结构如下图所示。
- Controller 接受上层 view 传回的参数,进行处理之后,返回 view 所需要的数据
- Service 层主要处理核心业务逻辑,比如最常见的
合法性判断
,业务运算
,逻辑聚合
,封装返回值
等等 - Dao 层作为数据的中转站,对各类数据源进行整合。
只是整合数据源 不处理业务逻辑
- 然后就是具体的数据层
这个布局和设计是我个人比较习惯的方式。但是,系统设计从来都是一件仁则见仁,智者见智的事情。所以,最好是能找到一种适合自己风格的方式。
具体来看 大概是如下的层次结构:
数据库表 [3.5]
建表语句如下
CREATE TABLE `user` ( `id` int(10) NOT NULL AUTO_INCREMENT, `name` varchar(60) DEFAULT NULL, `gender` tinyint(4) DEFAULT NULL, `birthday` datetime DEFAULT NULL, `email` varchar(60) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) )DEFAULT CHARSET=utf8;
整体上有了个映像之后 我们一步一步拆开看吧
首先 最重要的是 - SpringBoot [4]
先让 SpringBoot 飞 [4.1]
1.我一般习惯性在根目录下新建一个叫做 Application.java
的文件作为 启动器
2.然后, 像所以 Java
程序一样,你需要一个 main
函数
3.在 Application
的头上戴个帽子(注解) @SpringBootApplication
这个注解其实等价于 @Configuration
+ @EnableAutoConfiguration
+ @ComponentScan
。用哪个得根据需求和喜好了,当然还有版本。
4.看起来应该是这个样子:
@ComponentScan(basePackages = {"com.hello.mybatis"}) @Configuration @EnableAutoConfiguration(exclude={ DataSourceAutoConfiguration.class}) public class Application { public static void main(String [] args) { SpringApplication.run(Application.class, args); } }
5.然后,amazing 的事情发生了: 你的 SpringBoot 项目已经可以启动运行了 - “妈妈再也不用担心 So easy 系列”
我常用的 SpringBoot 注解 [4.2]
SpringBoot 官方文档洋洋洒洒几百页。其实,你只要会 Java,再了解一下这些注解的作用。使用起来也就没啥障碍了。平时需要什么的时候,再查就好了。然后,我平时常用的注解有这么几个:
@Configuration
@Bean
@Value
@Service
@Controller
@Repository
@Inject
@Named
…
引入外部配置文件 和 配置数据源的 两种方式 [4.3]
其实, 针对外部配置文件, Spring
框架已经帮我们考虑了很多。不过有时候根据项目需要,我们也得手动导入一些配置文件信息。所以,这里特别就这两种方式,进行简单说明。
1.自定义的配置文件根目录(比如示例项目的 /conf 目录)
这个目录位置可以在 pom.xml
文件中进行配置,比如:
<build> <resources> <resource> <directory>conf</directory> </resource> </resources> </build>
2. SpringBoot
自动导入配置文件 自动注入数据源
在此根目录下新建一个 application.properties
文件。这个文件里的参数值会被 SpringBoot
自动载入。然后可以在项目中通过注解 @Value
进行访问 例如:
@Value("${host:127.0.0.1}") private String HOST;
这里 :
后为未找到该参数项时的默认值。如果不添加默认值,那么,读取不到该参数时,会抛 IllegalArgumentException
。载入时,参数类型不匹配也会抛异常。
至于自动载入数据源的问题,大体类似。首先在 pom.xml
里面添加关于 jdbc 和 MySQL connector 的 dependency。然后,在 application.properties
文件中根据 SpringBoot 的命名规范添加相关信息就可以了。完成之后,在项目中,就可以用 @Inject DataSource dataSource
的方式访问这个数据源了。通过这种方式,也可以配置多个数据源。
application.properties
中数据源配置信息的命名格式
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc: mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root
3.手动载入配置文件 并 配置数据源
新建配置文件(比如 jdbc.properties
)。然后在项目中,进行手动载入和调用。具体如下:
jdbc.properties
dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource dataSource.url=jdbc: mysql://localhost:3306/test dataSource.user=root dataSource.password=root dataSource.cachePrepStmts=true dataSource.prepStmtCacheSize=250 dataSource.prepStmtCacheSqlLimit=2048
配置 DataSource
这里推荐一个高性能的 DataSource
- HikariDataSource
为了使用这个 DataSource,需要添加 Dependency:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>2.5.1</version> </dependency>
数据源配置:
@Bean(name = "userDataSource") public DataSource userDataSource() { HikariDataSource mysqlDS = null; try { Resource resource = new ClassPathResource("jdbc.properties"); Properties props = PropertiesLoaderUtils.loadProperties(resource); HikariConfig config = new HikariConfig(props) { }; mysqlDS = new HikariDataSource(config); log.info("[Mybatis] - Database connection pool created!"); } catch (IOException e) { log.error("[Mybatis] - Error connecting data source", e); } return mysqlDS; }
Mybatis 必知必会 [5]
通过 Mapper 访问数据库 [5.1]
这里有两层意思:
a) 具体的 sql 都写在 mapper 中
b) 上层需要获得 mapper 去访问数据库
sql 写在哪儿? [5.2]
a) 可以写在定义 mapper 的 interface 中
b) 可以写在 *Mapper.xml 中
这里使用第二种方法,个人感觉写在 xml 文件中,能更加方便的使用 mybatis 的 动态 sql
特性
SqlSession [5.3]
我们需要 SqlSession 去打开一个会话。之后,你可以使用它来执行映射语句,提交或回滚连接,最后,当不再需要它的时候, 你可以关闭 session。 使用 MyBatis-Spring 之后, 你的 bean 可以通过一个线程安全的 SqlSession 来注入,基于 Spring 的事务配置 来自动提交,回滚,关闭 session。 不再需要手动关闭 SqlSession
。
SqlSessionTemplate [5.4]
为了获得 SqlSession。我们需要 SqlSessionTemplate
。SqlSessionTemplate 是 MyBatis-Spring 的核心。 这个类负责管理 MyBatis 的 SqlSession, 调用 MyBatis 的 SQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。当调用 SQL 方法时, 包含从映射器 getMapper() 方法返回的方法, SqlSessionTemplate 将会保证使用的 SqlSession 是和当前 Spring 的事务相关的。此外,它管理 session 的生命 周期,包含必要的关闭,提交或回滚操作。
@Bean(name = "userSqlSessionTemplate") public SqlSessionTemplate ringsAccountSqlSessionTemplate(@Named("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { log.info("[Mybatis] - Loaded the SqlSessionTemplate with name userSqlSessionTemplate!"); return new SqlSessionTemplate(sqlSessionFactory); }
SqlSessionFactory [5.5]
为了获得 SqlSessionTemplate
。我们需要 SqlSessionFactory
。为了获得 SqlSessionFactory
。我们需要一个 DataSource
。DataSource 的注入方法,在前面已经阐述过了,此处就不在赘述。
@Bean(name = "userSqlSessionFactory") public SqlSessionFactory userSqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(userDataSource()); sqlSessionFactoryBean.setTypeAliasesPackage("com.hello.mybatis.repository.domain"); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mybatis/*Mapper.xml")); log.info("[Mybatis] - Loaded the userSqlSessionFactory!"); return (SqlSessionFactory) sqlSessionFactoryBean.getObject(); }
请注意他们的依赖次序 mapper -> SqlSession -> SqlSessionTemplate -> SqlSessionFactory -> DataSource
准备工作完毕之后 [haha] [6]
定义 POJO 模型 [6.1]
毕竟示例程序,越简单越好。这里就整个最普通的 User 模型。考虑到篇幅,getter, setter 和 constructer 我没贴出来。感兴趣的可以直接去看源码。 User
里的 id 作为唯一标识这个对象的 自增
主键。
public class User { private Integer id; private String name; private int gender; private Timestamp birthday; private String email; private String phone; }
UserMapper.xml 里面有些什么 [6.2]
1.先贴代码:
<?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="com.hello.mybatis.repository.mybatis.mapper.UserMapper"> <resultMap id="userResultMap" type="com.hello.mybatis.repository.pojo.User"> <id property="id" column="id" javaType="java.lang.Integer" jdbcType="INTEGER"/> <result property="name" column="name"/> <result property="gender" column="gender"/> <result property="birthday" column="birthday"/> <result property="email" column="email"/> <result property="phone" column="phone"/> </resultMap> <insert id="createUser" parameterType="com.hello.mybatis.repository.pojo.User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user ( name, gender, birthday, email, phone ) VALUES ( #{name}, #{gender}, #{birthday}, #{email}, #{phone} ); </insert> <select id="selectUserById" parameterType="int" resultMap="userResultMap"> SELECT * FROM user WHERE id=#{id} </select> <select id="listUserByIdList" resultMap="userResultMap"> SELECT * FROM user WHERE id IN <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select> </mapper>
2. namespace
指向定义的 mapper
。这个是重点,也是很容易踩进去的坑。要特别注意。另外一点就是,mapper 接口里是可以直接定义 sql
的,具体请 Google。
3. resultMap
定义的是数据库表和模型对象之间的映射关系。 mybatis
会根据 resultMap 里的定义进行字段和对象属性之间的映射和转换。这里的 type
指向自定义的模型对象。
4. insert
标签里的 useGeneratedKeys="true" keyProperty="id"
会返回生成的自增主键值。 #{}
操作符用来获取对象相应属性的值。
5. parameterType
是传入数据的类型。
6. foreach
标签会遍历传入的 list
。如果传入的 parameterType
为 map
,这里的 collection
可为自定义的 list
名称。
UserMapper 接口 [6.3]
这个接口起着承上启下的作用。里面可以直接定义 sql
语句。如果不在此接口内的方法上方定义 sql
,那么里面的方法名,参数列表,返回值必须与 *Mapper.xml
里面各种 sql 标签一致。sql 标签的 id
值即为方法名。
public interface UserMapper { int createUser(final User user); User selectUserById(final int id); List<User> listUserByIdList(final Collection<Integer> idList); }
MybatisRepository 的实现类 [6.4]
同样,考虑到篇幅的问题,这里只列举了一个方法作为示范,具体请查看源代码。这个类里请注意这几点:
- 注入 SqlSession
- 通过 SqlSession 获取
UserMapper
- 用 Mapper 来访问数据库
@Repository public class UserMybatisRepositoryImpl implements UserMabatisRepository { @Inject private SqlSession sqlSession; private final static Logger log = LoggerFactory.getLogger(UserMybatisRepositoryImpl.class); private UserMapper getMapper() { return this.sqlSession.getMapper(UserMapper.class); } @Override public int createUser(User user) throws DatabaseException { int result = getMapper().createUser(user); if (result <= 0) { log.error("[createUser] - Fail to create user[{}]", user.toString()); throw new DatabaseException("[createUser] - Fail to create user: " + user.toString()); } return 0; } }
Service 方法 [6.5]
因为这个项目逻辑相对简单,所以 Service
中并未出现很多业务处理的操作。只是简单的判断了下 Request 对象
,对 Response
进行了封装。更多的只是从注入的 MybatisRepository
直接访问数据库。
@Service public class UserServiceImpl implements UserService { @Inject private UserMabatisRepository userMabatisRepository; @Override public BaseResp createUser(CreateUserReq req) { if (req == null) { return new BaseResp(false, "Null Request!"); } User user = new User(req.getName(), req.getGender(), req.getBirthday(), req.getEmail(), req.getPhone()); try { userMabatisRepository.createUser(user); } catch (DatabaseException e) { return new BaseResp(false, e.toString()); } return new BaseResp(true, "OK"); } }
简单粗暴的 Controller [6.6]
@Controller @RequestMapping("/user") public class UserController { @Inject private UserService userService; @ResponseBody @RequestMapping(value = "/create-user", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") public BaseResp createUser(@RequestBody CreateUserReq req) { return userService.createUser(req); } }
写在最后
再一次:
源码地址 -> https://github.com/GavinLee1/HelloMybatis.git
References
[1] GitBook - SpringBoot 参考指南 Spring-Boot-Reference-Guide
[2] What’s Mybatis
[3] Mybatis - Spring
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
下一篇: Java 单例模式
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论