Mybatis
Mybatis作为本人最喜欢的持久层框架,当然得整合进来啦!!!
- Mybatis需要程序猿自己编写增删改查所需要的SQL语句,而Hibernate可以通过对象关系模型实现对数据库的操作,可以完全脱离SQL,但正因如此Mybatis可以根据业务最大程度优化SQL语句来达到良好的性能。Mybatis也有第三方工具提供类似Hibernate的功能,比如Mybatis-plus,这里暂时不介绍
- Mybatis的可移植性非常一般...虽说有支持,但局限性还是比较大的,而Hibernate所使用的HQL则可以转换成对应数据库的SQL。虽说好像很厉害,但一般来说换数据库的操作还是比较少的。
- 还有其他区别,但我就不写
SpringBoot整合Mybatis
添加jar依赖---pom.xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
导入Mybatis相关依赖,mybatis-spring-boot-starter是把Mybatis.jar和mybatis-spring.jar整合在了一起。
配置Mybatis
配置Mybatis常用两种方式,一种使用SpringBoot配置文件方式配置数据库连接及Mybatis;另一种则是使用java config配置(代码方式),这也是我比较喜欢的一种方式。
application.yml方式
1、在application.yml配置数据库连接信息及Mybatis
spring:
datasource:
data-source-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/technology-integration
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapping/ *.xml
type-aliases-package: com.viu.technology.po
2、在TechnologyIntegrationApplication.class中添加MapperScan注解,该注解用于扫描Mapper接口所在的包
@SpringBootApplication
@MapperScan("com.viu.technology.mapper")
public class TechnologyIntegrationApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(TechnologyIntegrationApplication.class, args);
}
}
java config方式
在config.datasource包中创建MybatisConfig类
@Configuration
@MapperScan("com.viu.technology.mapper")
public class MybatisConfig extends HikariConfig{
@Autowired
private Environment env;
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource1.jdbc")
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(env.getProperty("datasource1.jdbc.driverClassName"));
dataSource.setJdbcUrl(env.getProperty("datasource1.jdbc.url"));
dataSource.setUsername(env.getProperty("datasource1.jdbc.username"));
dataSource.setPassword(env.getProperty("datasource1.jdbc.password"));
dataSource.setMaximumPoolSize(100);
dataSource.setPoolName("hikari-");
return dataSource;
}
@Bean
@Primary
public SqlSessionFactory sqlSessionFactory( DataSource dataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(dataSource);
fb.setTypeAliasesPackage("com.viu.technology.po");
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/*.xml"));
return fb.getObject();
}
}
application.yml
datasource1.jdbc:
driverClassName: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/technology-integration
username: root
password: 123456
min-idle: 10
- @MapperScan("com.viu.technology.mapper") :扫描Mapper接口所在的包
- @Primary :在拥有多个同类型Bean时,SpringBoot自动注入会选择带有该注解的Bean,否则自动注入将会提示注入失败,原因是因为IOC中拥有多个同类型的Bean,而SpringBoot无法判断使用哪个Bean,导致异常发生。
- Environment:可获取到所有的配置资源
- @ConfigurationProperties(prefix = "datasource1.jdbc") :加载application..yml以datasource1.jdbc开头的配置
- fb.setTypeAliasesPackage("com.viu.technology.po"):设置该包下的所有类可以在mapper.xml中直接使用类名而非全类名
这里需要使用@Primary注解是因为SpringBoot默认配置了SqlSessionFactory和DataSource,即SpringBoot的IOC容器启动时会自动将这两个Bean注入进来,而我们选择java config方式配置数据库时则会与SpringBoot默认配置的DataSource Bean产生冲突,@Primary注解则是用来告诉容器优先选择java config配置的Bean。不过该方式并不是最好的解决方案,因为SpringBoot默认配置的DataSource Bean我们并没有使用,我们可以在启动类中取消SpringBoot自动配置的DataSource,这样就不需要加@Primary注解,还能提高应用的启动速度。
@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class TechnologyIntegrationApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(TechnologyIntegrationApplication.class, args);
}
}
SpringBoot配置Mybatis就完成了,接下来写个注册用户的案例
步骤:
- 创建一条新的用户数据,userId使用Mybatis生成UUID进行插入,并返回
- 创建一条新的用户角色数据,简单业务,ID自增
- 创建成功并返回
编写SQL语句
UserMapper.xml
selectKey 会将MySQL select的结果放入到传入的model的主键里面,
keyProperty 对应的model中的主键的属性名,这里是 user 中的id,因为它跟数据库的主键对应
order AFTER 表示 SELECT在insert执行之后执行,多用与自增主键;
BEFORE 表示 SELECT在insert执行之前执行, 这种适合那种主键不是自增的类型
<insert id="insert" parameterType="com.viu.technology.po.User">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
select replace(uuid(), '-', '') as id from dual
</selectKey>
insert into t_user (id,name, phone,password)
values (#{id},#{name}, #{phone}, #{password}
)
</insert>
RoleMapper.xml
<insert id="insert" parameterType="com.viu.technology.po.Role">
insert into t_role (role_name, user_id)
values ( #{roleName,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER})
</insert>
编写DAO类
在项目的dao包创建UserDao接口类
public interface UserDao {
User selectByPrimaryKey(String id);
}
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private UserMapper userMapper;
public User selectByPrimaryKey(String id) {
return userMapper.selectByPrimaryKey(id);
}
}
public interface RoleDao {
Role insertRole(Role role);
}
@Repository
public class RoleDaoImpl implements RoleDao {
@Autowired
private RoleMapper roleMapper;
@Override
public Role insertRole(Role role) {
if (roleMapper.insert(role)>0) {
return role;
}
return null;
}
}
UserDao的实现类需要加入@Repository,一是为了把该类注册为Bean,二是该注解支持处理数据库操作产生的异常
编写Service类
public interface UserService {
User registerUser(User user);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Transactional
@Override
public User registerUser(User user) {
User userRes = userDao.insertUser(user);
Role roleRes = roleDao.insertRole(new Role("普通群众", user.getId()));
List list = new ArrayList();
list.add(roleRes);
if (null != userRes && null != roleRes) {
userRes.setRoles(list);
return user;
}
return null;
}
}
编写Result类(用于统一返回结果)
public class Result<T> {
private String code;
private String msg;
private T data;
public Result() {}
public Result(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
this.data = data;
}
public Result(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
}
public static Result success() {return new Result(ResultCode.SUCCESS);}
public static Result success(Object data) {return new Result(ResultCode.SUCCESS, data); }
public static Result fail(ResultCode resultCode,String msg){return new Result(resultCode, msg);}
public static Result fail(ResultCode resultCode){return new Result(resultCode);}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
public class ResultCode {
private String code;
private String msg;
public static ResultCode SUCCESS = new ResultCode("0000", "success");
public static ResultCode DATA_CHECK_FAILURE = new ResultCode("1000", "数据校验失败");
public static ResultCode DATA_INSERT_FAILURE = new ResultCode("1001", "数据插入失败");
public static ResultCode SECKILL_END = new ResultCode("2001", "秒杀结束");
public static ResultCode SMS_SEND_FAIL = new ResultCode("3001", "短信发送失败");
public static ResultCode SMS_CODE_VALID_FAIL = new ResultCode("3002", "短信校验失败");
public static ResultCode SMS_QUERY_NOT_FOUND = new ResultCode("3003", "找不到该短信");
public static ResultCode USER_REGISTER_FIAL = new ResultCode("4000", "用户注册失败");
public static ResultCode USER_LOGIN_FIAL = new ResultCode("401", "用户身份验证失败");
public static ResultCode USER_PERMISSION_DENIED = new ResultCode("403", "用户账号权限不足");
public ResultCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {return code;}
public void setCode(String code) {this.code = code;}
public String getMsg() {return msg;}
public void setMsg(String msg) {this.msg = msg;}
}
编写Controller类
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceImpl userService;
@PostMapping("/register")
public Result insertUser(@RequestBody User user) {
User result = userService.registerUser(user);
if (null != result) {
return Result.success(result);
}
return Result.fail(ResultCode.USER_REGISTER_FIAL);
}
}
测试
启动SpringBoot应用,在postman中测试该地址