【发布时间】:2018-08-07 13:42:03
【问题描述】:
我正在使用 Spring Boot 2.X 和 Hibernate 5 来连接不同服务器上的两个不同的 MySQL 数据库(Bar 和 Foo)。我正在尝试从 REST 控制器中的方法列出实体的所有信息(自己的属性和 @OneToMany 和 @ManyToOne 关系)。
我遵循了几个教程来执行此操作,因此,我能够获取我的 @Primary 数据库 (Foo) 的所有信息,但是,在检索 @ 时,我的辅助数据库 (Bar) 总是出现异常987654324@ 套。如果我将 @Primary 注释交换到 Bar 数据库,我可以从 Bar 数据库中获取数据,但不能从 Foo 数据库中获取数据。有没有办法解决这个问题?
这是我得到的例外:
...w.s.m.s.DefaultHandlerExceptionResolver :
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException:
Could not write JSON document: failed to lazily initialize a collection of role:
com.foobar.bar.domain.Bar.manyBars, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]-com.foobar.bar.domain.Bar["manyBars"]);
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
failed to lazily initialize a collection of role:
com.foobar.bar.domain.Bar.manyBars, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.foobar.bar.domain.Bar["manyBars"])
我的 application.properties:
# MySQL DB - "foo"
spring.datasource.url=jdbc:mysql://XXX:3306/foo?currentSchema=public
spring.datasource.username=XXX
spring.datasource.password=XXX
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# MySQL DB - "bar"
bar.datasource.url=jdbc:mysql://YYYY:3306/bar?currentSchema=public
bar.datasource.username=YYYY
bar.datasource.password=YYYY
bar.datasource.driver-class-name=com.mysql.jdbc.Driver
# JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
我的@PrimaryDataSource 配置:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {"com.foobar.foo.repo"})
public class FooDbConfig {
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.foobar.foo.domain")
.persistenceUnit("foo")
.build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
我的辅助数据源配置:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "barEntityManagerFactory",
transactionManagerRef = "barTransactionManager", basePackages = {"com.foobar.bar.repo"})
public class BarDbConfig {
@Bean(name = "barDataSource")
@ConfigurationProperties(prefix = "bar.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "barEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(
EntityManagerFactoryBuilder builder, @Qualifier("barDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.foobar.bar.domain")
.persistenceUnit("bar")
.build();
}
@Bean(name = "barTransactionManager")
public PlatformTransactionManager barTransactionManager(
@Qualifier("barEntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
return new JpaTransactionManager(barEntityManagerFactory);
}
}
REST 控制器类:
@RestController
public class FooBarController {
private final FooRepository fooRepo;
private final BarRepository barRepo;
@Autowired
FooBarController(FooRepository fooRepo, BarRepository barRepo) {
this.fooRepo = fooRepo;
this.barRepo = barRepo;
}
@RequestMapping("/foo")
public List<Foo> listFoo() {
return fooRepo.findAll();
}
@RequestMapping("/bar")
public List<Bar> listBar() {
return barRepo.findAll();
}
@RequestMapping("/foobar/{id}")
public String fooBar(@PathVariable("id") Integer id) {
Foo foo = fooRepo.findById(id);
Bar bar = barRepo.findById(id);
return foo.getName() + " " + bar.getName() + "!";
}
}
Foo/Bar 存储库:
@Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
Foo findById(Integer id);
}
@Repository
public interface BarRepository extends JpaRepository<Bar, Long> {
Bar findById(Integer id);
}
@Primary 数据源的实体。第二个数据源的实体是一样的(只是改了类名):
@Entity
@Table(name = "foo")
public class Foo {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column(name = "name")
private String name;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "foo")
@JsonIgnoreProperties({"foo"})
private Set<ManyFoo> manyFoos = new HashSet<>(0);
// Constructors, Getters, Setters
}
@Entity
@Table(name = "many_foo")
public class ManyFoo {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column(name = "name")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnoreProperties({"manyFoos"})
private Foo foo;
// Constructors, Getters, Setters
}
最后,我的应用程序 main:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
值得注意的是,解决方案应为两个数据库保留 Lazy 属性以保持最佳性能。
编辑 1:如果两个目录(MySQL 术语中的“数据库”)在同一个数据库(“服务器”)中,Rick James 解决方案有效!!
问题仍然存在,当目录(MySQL 数据库)位于不同的数据库(服务器)中并试图保持 Lazy 属性时
非常感谢。
【问题讨论】:
-
哪个控制器方法抛出这个异常? "/foobar/{id}" ?
-
@hovanessyan 从第二个数据库(Bar)中检索 OneToMany 对象的所有方法,例如“/bar”。根据您的第二个问题,方法“/foobar/{id}”不会抛出任何异常,因为它只返回对象 bar 的名称而不是 ManyToOne Foo foo。
-
您能否也发布您的存储库?
-
我编辑了问题以包含两个存储库。
标签: java mysql spring hibernate spring-boot