admin

Springboot 项目 Spring Data JPA 与 Mybatis 的混用
前言以前在项目中,都是单独的使用某种 ORM 或者其他的数据库操作框架来进行数据库操作。那么在项目中同时使用 Hi...
扫描右侧二维码阅读全文
11
2018/10

Springboot 项目 Spring Data JPA 与 Mybatis 的混用

前言

以前在项目中,都是单独的使用某种 ORM 或者其他的数据库操作框架来进行数据库操作。
那么在项目中同时使用 Hibernate 与 Mybatis 会不会出现问题呢?

分析

一开始最容易想到的就是事务问题,如果一个业务方法中需要用到事务,并且方法中同时使用 Hibernate 与 Myabtis 来操作数据库,会不会出现事务无法回滚?
之所以有这样的考虑疑问是因为 Hibernate 与 Mybatis 在同一个业务方法中使用的是不同的 JDBC Connection。

其次,就是数据源的问题,Hibernate 与 Mybatis 是共用一个 DataSource 还是分别使用一个数据源?

最后,Dao 的设计问题,假设上面的问题都不是问题,那么最后就是如何优雅地设计 Dao 接口。

实践

这里以一个简单的操作 User 表来做测试。
首先,新建一个 Sptingboot 项目:
微信截图_20181011181555.png

导入到 Idea,新建包以及类与接口,最终项目结构如下:
微信截图_20181011181806.png

application.properties如上图所示,竟然是要同时使用HibernateMybatis,那么久直接把它们两个都配上试试。
先来看看实体类User:

package net.che_mi.hmtest.entity;

import javax.persistence.*;

@Entity(name = "tb_user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    @Column
    private Integer age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

和单独使用Spring Data JPA时没什么区别。
再看看Repository与 Mybatis 的MapperUserRepository

package net.che_mi.hmtest.dao.hibernate;

import net.che_mi.hmtest.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

UserMapper.java

package net.che_mi.hmtest.dao.mybatis;

import net.che_mi.hmtest.entity.User;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserMapper {

    List<User> selectAll();

    void save(User user);
}

UserMapper.xml

<?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="net.che_mi.hmtest.dao.mybatis.UserMapper" >

    <select id="selectAll" resultType="net.che_mi.hmtest.entity.User">
        select * from tb_user
    </select>

    <insert id="save" parameterType="net.che_mi.hmtest.entity.User">
        insert into tb_user (name, age) values (#{name}, #{age})
    </insert>
</mapper>

以上的一些配置与代码都和单独地使用Spring Data JPA或者Mybatis没什么两样,那么项目能不能跑起来呢?
直接运行发现没有问题。
再试试同时使用UserRepositoryUserMapper操作数据也没有任何问题。

至于事务问题,最后再说,现在竟然同时使用没有问题,那么如何优雅地使用这两个数据库操作接口?
显然,如果在一个Service里面同时注入这两个Dao接口是没有问题的,但是不够优雅,注入两个不同的接口就意味着有两个对象名称,每次使用还要去看看方法是在哪个接口里面。

所以我的做法就是使用组合模式再封装一层,也就是上面项目结构图中的UserDao类,当然,你也可以把UserDao做成接口,然后实现它,我这里为了方便就直接定义了一个UserDao类:

package net.che_mi.hmtest.dao;

import net.che_mi.hmtest.dao.hibernate.UserRepository;
import net.che_mi.hmtest.dao.mybatis.UserMapper;
import net.che_mi.hmtest.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class UserDao {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserRepository userRepository;

    /***
     * 使用 Hibernate 保存用户
     * @param user
     */
    public void saveUseJpa(User user) {
        userRepository.save(user);
    }

    /***
     * 使用 Mybatis 保存用户
     * @param user
     */
    public void saveUseMybatis(User user) {
        userMapper.save(user);
    }

    public List<User> getAll() {
        return userMapper.selectAll();
    }
}

然后我们的UserServiceImpl中就能这样使用:

package net.che_mi.hmtest.service.impl;

import net.che_mi.hmtest.dao.UserDao;
import net.che_mi.hmtest.entity.User;
import net.che_mi.hmtest.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserDao userDao;

    @Override
    public List<User> getAllUser() {
        return userDao.getAll();
    }

    @Override
    @Transactional
    public void saveUser(User user) {

        userDao.saveUseJpa(user);

        userDao.saveUseMybatis(user);

        //int x = 1 / 0;  //测试事务
    }
}

这样一来我们在代码中就只要注入UserDao就行了。

最后一个问题就是数据库事务,注意看上面UserServiceImpl中的saveUser()方法,方法名上面使用@Transactional开启事务,方法里面分别调用saveUseJpa()saveUseMybatis()来同时使用HibernateMybatis,然后加上一行异常代码:int x = 1 / 0;,观察数据库发现没有一条数据插入成功,即事务生效。

Last modification:October 11th, 2018 at 06:35 pm
If you think my article is useful to you, please feel free to appreciate

One comment

  1. fookwood

    其实这里的UserDao就相当于一个@Repository咯?

Leave a Comment