栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何设置Hibernate来读/写不同的数据源?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何设置Hibernate来读/写不同的数据源?

Spring提供了DataSource的变体,称为

AbstractRoutingDatasource
。它可以代替标准DataSource实现使用,并启用一种机制来确定在运行时为每个操作使用哪个具体DataSource。你需要做的就是扩展它并提供抽象
determineCurrentLookupKey
方法的实现。在这里实现你的自定义逻辑来确定具体的数据源。返回的对象用作查找键。它通常是String或en Enum,在Spring配置中用作限定符(详细信息如下)。

package website.fedulov.routing.RoutingDataSourceimport org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class RoutingDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DbContextHolder.getDbType();    }}

你可能想知道DbContextHolder对象是什么,它如何知道要返回哪个DataSource标识符?请记住,

determineCurrentLookupKey
每当TransactionsManager请求连接时,该方法都会被调用。重要的是要记住,每个事务都与单独的线程“关联”。更准确地说,Transactions Manager将Connection绑定到当前线程。因此,为了将不同的事务分派给不同的目标数据源,我们必须确保每个线程都可以可靠地标识将使用哪个数据源。这使得利用ThreadLocal变量将特定的DataSource绑定到Thread并因此绑定到Transaction变得很自然。这是这样做的:

public enum DbType {   MASTER,   REPLICA1,}public class DbContextHolder {   private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<DbType>();   public static void setDbType(DbType dbType) {       if(dbType == null){throw new NullPointerException();       }      contextHolder.set(dbType);   }   public static DbType getDbType() {      return (DbType) contextHolder.get();   }   public static void clearDbType() {      contextHolder.remove();   }}

如你所见,你还可以使用枚举作为键,Spring会根据名称正确解析它。关联的DataSource配置和键如下所示:

  ....<bean id="dataSource" > <property name="targetDataSources">   <map key-type="com.sabienzia.routing.DbType">     <entry key="MASTER" value-ref="dataSourceMaster"/>     <entry key="REPLICA1" value-ref="dataSourceReplica"/>   </map> </property> <property name="defaultTargetDataSource" ref="dataSourceMaster"/></bean><bean id="dataSourceMaster" >  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  <property name="url" value="${db.master.url}"/>  <property name="username" value="${db.username}"/>  <property name="password" value="${db.password}"/></bean><bean id="dataSourceReplica" >  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  <property name="url" value="${db.replica.url}"/>  <property name="username" value="${db.username}"/>  <property name="password" value="${db.password}"/></bean>

此时,你可能会发现自己正在执行以下操作:

@Servicepublic class BookService {  private final BookRepository bookRepository;  private final Mapper    mapper;  @Inject  public BookService(BookRepository bookRepository, Mapper mapper) {    this.bookRepository = bookRepository;    this.mapper = mapper;  }  @Transactional(readonly = true)  public Page<BookDTO> getBooks(Pageable p) {    DbContextHolder.setDbType(DbType.REPLICA1);   // <----- set ThreadLocal DataSource lookup key      // all connection from here will go to REPLICA1    Page<Book> booksPage = callActionRepo.findAll(p);    List<BookDTO> pContent = CollectionMapper.map(mapper, callActionsPage.getContent(), BookDTO.class);    DbContextHolder.clearDbType();    // <----- clear ThreadLocal setting    return new PageImpl<BookDTO>(pContent, p, callActionsPage.getTotalElements());  }  ...//other methods

现在,我们可以控制将使用哪个DataSource并转发请求。看起来不错!

…或者是吗?首先,那些对魔术DbContextHolder的静态方法调用确实很出色。它们看起来不属于业务逻辑。而且他们没有。它们不仅不能传达目的,而且似乎很脆弱且容易出错(如何忘记清理dbType呢?)。而且,如果在setDbType和cleanDbType之间引发异常怎么办?我们不能只是忽略它。我们必须绝对确保要重置dbType,否则返回到ThreadPool的Thread可能处于“中断”状态,试图在下一个调用中写入副本。所以我们需要这个:

  @Transactional(readonly = true)  public Page<BookDTO> getBooks(Pageable p) {    try{      DbContextHolder.setDbType(DbType.REPLICA1);   // <----- set ThreadLocal DataSource lookup key        // all connection from here will go to REPLICA1      Page<Book> booksPage = callActionRepo.findAll(p);      List<BookDTO> pContent = CollectionMapper.map(mapper, callActionsPage.getContent(), BookDTO.class);       DbContextHolder.clearDbType();    // <----- clear ThreadLocal setting    } catch (Exception e){      throw new RuntimeException(e);    } finally {       DbContextHolder.clearDbType();    // <----- make sure ThreadLocal setting is cleared  }    return new PageImpl<BookDTO>(pContent, p, callActionsPage.getTotalElements());  }

kes

>_<
!这绝对不像我想在每个只读方法中使用的内容。我们可以做得更好吗?当然!这种“在方法开始时先做某事,然后在方法末尾做某事”的模式应该引起人们的注意。救援方面!

不幸的是,这篇文章已经太久了,无法涵盖定制方面的话题。你可以使用此链接跟进使用方面的详细信息。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/398706.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号