欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解基于Spring Boot与Spring Data JPA的多数据源配置

程序员文章站 2024-02-18 11:43:10
由于项目需要,最近研究了一下基于spring boot与spring data jpa的多数据源配置问题。以下是传统的单数据源配置代码。这里使用的是spring的annot...

由于项目需要,最近研究了一下基于spring boot与spring data jpa的多数据源配置问题。以下是传统的单数据源配置代码。这里使用的是spring的annotation在代码内部直接配置的方式,没有使用任何xml文件。

@configuration 
@enablejparepositories(basepackages = "org.lyndon.repository") 
@enabletransactionmanagement 
@propertysource("classpath:application.properties") 
public class jpaconfig { 
   
  private static final string database_driver = "db.driver"; 
  private static final string database_url = "db.url"; 
  private static final string database_user = "db.user"; 
  private static final string database_password = "db.password"; 
  private static final string packages_to_scan = "packages.to.scan"; 
  private static final string hibernate_dialect = "hibernate.dialect"; 
  private static final string hibernate_show_sql = "hibernate.show.sql"; 
   
  @resource 
  private environment env; 
   
  @bean 
  public datasource datasource() { 
    druiddatasource source = new druiddatasource(); 
    source.setdriverclassname(env.getrequiredproperty(database_driver)); 
    source.seturl(env.getrequiredproperty(database_url)); 
    source.setusername(env.getrequiredproperty(database_user)); 
    source.setpassword(env.getrequiredproperty(database_password)); 
    return source; 
  } 
   
  @bean 
  public localcontainerentitymanagerfactorybean entitymanagerfactory() { 
    localcontainerentitymanagerfactorybean factory = new localcontainerentitymanagerfactorybean(); 
    factory.setdatasource(datasource()); 
    factory.setpersistenceproviderclass(hibernatepersistenceprovider.class); 
    factory.setpackagestoscan(env.getrequiredproperty(packages_to_scan)); 
    factory.setjpaproperties(hibernateproperties()); 
    factory.afterpropertiesset(); 
    return factory; 
  } 
   
  @bean 
  public platformtransactionmanager transactionmanager() { 
    jpatransactionmanager manager = new jpatransactionmanager(); 
    manager.setentitymanagerfactory(entitymanagerfactory().getobject()); 
    return manager; 
  } 
   
  @bean 
  public hibernateexceptiontranslator hibernateexceptiontranslator() { 
    return new hibernateexceptiontranslator(); 
  } 
   
  private properties hibernateproperties() { 
    properties properties = new properties(); 
    properties.put(hibernate_dialect, env.getrequiredproperty(hibernate_dialect)); 
    properties.put(hibernate_show_sql, env.getrequiredproperty(hibernate_show_sql)); 
    return properties; 
  } 
   
} 

但是这一配置是不能简单地扩展到多数据源配置的,因为spring boot默认会为开发人员做很多工作,而这些工作与多数据源的配置相冲突,因此需要修改原来的配置内容。网上有很多讲解多数据源配置的文章,但是这些文章大多使用的是xml配置的方式,而且没有使用spring boot等比较新的spring框架技术(比如很多人使用实现abstractroutingdatasource这一spring提供的抽象类的方式,还需要切面的支持,无疑是相当繁琐的),已经不适用于在最新的工程项目中使用了,因为最新的spring框架可以为我们完成很多事情,我们只需要去适应新的方法即可。为此,我通过研究spring的官方文档和不断调试,实现了新的多数据源配置的方法,在此贴出,仅供参考。

首先,spring的jpa是直接支持多数据源配置的,因此我们可以在配置文件或者代码中直接配置多个数据源。由于多数据源配置可能会共享一些配置信息,因此使用继承体系实现这种配置是最合适的。在例子中,我的orm框架使用的是hibernate,而与hibernate相关的配置信息我都放在了配置基类basejpaconfg中,代码如下。

@configuration 
@enabletransactionmanagement 
@propertysource("classpath:application.properties") 
public class basejpaconfig { 
 
  private static final string hibernate_dialect = "hibernate.dialect"; 
  private static final string hibernate_show_sql = "hibernate.show.sql"; 
 
  @resource 
  protected environment env; 
 
  @bean 
  public hibernateexceptiontranslator hibernateexceptiontranslator() { 
    return new hibernateexceptiontranslator(); 
  } 
 
  protected properties hibernateproperties() { 
    properties properties = new properties(); 
    properties.put(hibernate_dialect, env.getrequiredproperty(hibernate_dialect)); 
    properties.put(hibernate_show_sql, env.getrequiredproperty(hibernate_show_sql)); 
    return properties; 
  } 
 
} 

该类提供hibernate相关信息。它有两个子类,分别是springjpaconfig以及mysqljpaconfig,分别配置了两个数据源,连接到两个不同的数据库schema,代码如下。

@configuration 
@enablejparepositories(basepackages = {"org.lyndon.repository1"}, entitymanagerfactoryref = "springentitymanagerfactory", 
    transactionmanagerref = "springtransactionmanager") 
public class springjpaconfig extends basejpaconfig { 
 
  private static final string database_driver = "db.driver"; 
  private static final string database_url = "db.url"; 
  private static final string database_user = "db.user"; 
  private static final string database_password = "db.password"; 
  private static final string packages_to_scan = "packages.to.scan"; 
 
  @bean 
  @configurationproperties(prefix = "datasource.primary") 
  public datasource datasource() { 
    druiddatasource source = new druiddatasource(); 
    source.setdriverclassname(env.getrequiredproperty(database_driver)); 
    source.seturl(env.getrequiredproperty(database_url)); 
    source.setusername(env.getrequiredproperty(database_user)); 
    source.setpassword(env.getrequiredproperty(database_password)); 
    return source; 
  } 
 
  @bean(name = "springentitymanagerfactory") 
  public localcontainerentitymanagerfactorybean springentitymanagerfactory() { 
    localcontainerentitymanagerfactorybean factory = new localcontainerentitymanagerfactorybean(); 
    factory.setdatasource(datasource()); 
    factory.setpersistenceproviderclass(hibernatepersistenceprovider.class); 
    factory.setpackagestoscan(env.getrequiredproperty(packages_to_scan).split(",")); 
    factory.setjpaproperties(hibernateproperties()); 
    factory.afterpropertiesset(); 
    return factory; 
  } 
 
  @bean(name = "springtransactionmanager") 
  @primary 
  public platformtransactionmanager transactionmanager() { 
    jpatransactionmanager manager = new jpatransactionmanager(); 
    manager.setentitymanagerfactory(springentitymanagerfactory().getobject()); 
    return manager; 
  } 
 
} 
@configuration 
@enablejparepositories(basepackages = {"org.lyndon.repository2"}, entitymanagerfactoryref = "mysqlentitymanagerfactory", 
    transactionmanagerref = "mysqltransactionmanager") 
public class mysqljpaconfig extends basejpaconfig { 
 
  private static final string database_driver = "db.driver"; 
  private static final string database_url = "db.url2"; 
  private static final string database_user = "db.user"; 
  private static final string database_password = "db.password"; 
  private static final string packages_to_scan = "packages.to.scan2"; 
 
  @bean 
  @configurationproperties(prefix = "datasource.secondary") 
  public datasource datasource2() { 
    druiddatasource source = new druiddatasource(); 
    source.setdriverclassname(env.getrequiredproperty(database_driver)); 
    source.seturl(env.getrequiredproperty(database_url)); 
    source.setusername(env.getrequiredproperty(database_user)); 
    source.setpassword(env.getrequiredproperty(database_password)); 
    return source; 
  } 
 
  @bean(name = "mysqlentitymanagerfactory") 
  public localcontainerentitymanagerfactorybean mysqlentitymanagerfactory() { 
    localcontainerentitymanagerfactorybean factory = new localcontainerentitymanagerfactorybean(); 
    factory.setdatasource(datasource2()); 
    factory.setpersistenceproviderclass(hibernatepersistenceprovider.class); 
    factory.setpackagestoscan(env.getrequiredproperty(packages_to_scan).split(",")); 
    factory.setjpaproperties(hibernateproperties()); 
    factory.afterpropertiesset(); 
    return factory; 
  } 
 
  @bean(name = "mysqltransactionmanager") 
  public platformtransactionmanager transactionmanager() { 
    jpatransactionmanager manager = new jpatransactionmanager(); 
    manager.setentitymanagerfactory(mysqlentitymanagerfactory().getobject()); 
    return manager; 
  } 
 
} 

两份配置代码使用了各自的数据源、实体管理对象以及事务管理对象。这里要注意的有两点,首先是实体管理对象。spring boot框架为方便开发者,默认会寻找名为“entitymanagerfactory”的bean作为实体管理的实现,但是我们这里使用了两个实体管理对象,名称也并不是默认的“entitymanagerfactory”,因此必须在各自的enablejparepositories的annotation中指明使用的实体管理对象。第二点,就是事务管理对象。spring boot会提供一个默认的事务管理对象的实现,但是我们在这里使用了两个不同的事务管理对象,因此我们也需要在各自的enablejparepositories的annotation中指明使用的事务管理对象。除此以外,我们还必须指定其中一个事务管理对象为主要对象(使用primary这一annotation),让spring能有主次地使用相应的事务管理对象。

以上就是本文的主要内容。关于service的配置等问题,由于和单数据源时是一样的,因此就不再赘述了。基于上述代码,我们就可以使用spring boot实现多数据源之间的无缝切换了,是不是很轻松?如果你不想使用代码配置的方式,也可以使用xml文件代替,配置的核心属性与上文相同,可以自己加以琢磨。希望对大家的学习有所帮助,也希望大家多多支持。