跨双数据库对象关联报错,请求排查JPA映射问题
问题背景
我在Play Framework项目里定义了两个分属不同数据库的实体类:Company(归属kuno库)和Project(归属emil库),移除关联注解时程序能正常运行,但添加@OneToMany和@ManyToOne后抛出CreationException,核心错误提示:
Error with association to [class models.kuno.Company] from [models.emil.Project.company]. Is class models.kuno.Company registered?
项目环境:Play 2.6.11、sbt 1.0.4、scala 2.12.4、sbt-play-ebean 4.1.0
实体类代码
Company.java(对应kuno数据库)
package models.kuno; import io.ebean.Finder; import io.ebean.Model; import models.emil.Project; import play.data.validation.Constraints; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import java.util.List; @Entity @Table(name = Company.TABLE_NAME) public class Company extends Model { public static final String TABLE_NAME = "company"; public static final Finder<Long, Company> find = new Finder<>(Company.class, "kuno"); @Id public Long id; @Constraints.Required public String title; @OneToMany(mappedBy = "company") public List<Project> projects; /** * Constructor. */ public Company() { } }
Project.java(对应emil数据库)
package models.emil; import io.ebean.Finder; import io.ebean.Model; import models.kuno.Company; import play.data.validation.Constraints; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = Project.TABLE_NAME) public class Project extends Model { public static final String TABLE_NAME = "project"; public static final Finder<Long, Project> find = new Finder<>(Project.class, "emil"); @Id public Long id; @Constraints.Required public String title; public int amount; @ManyToOne public Company company; /** * Constructor. */ public Project() { } }
配置文件(application.conf)
play.evolutions { db.default.enabled = true db.kuno.enabled = true db.emil.enabled = true } ebean { kuno = ["models.kuno.*"] emil = ["models.emil.*"] default = ["models.changer.*"] } db { default { driver = com.mysql.cj.jdbc.Driver logSql = true url = "mysql://***:***@127.0.0.1:3306/default?serverTimezone=CET&verifyServerCertificate=false&useSSL=false" } kuno { driver = com.mysql.cj.jdbc.Driver logSql = true url = "mysql://***:***@127.0.0.1:3306/kuno?serverTimezone=CET&verifyServerCertificate=false&useSSL=false" } emil { driver = com.mysql.cj.jdbc.Driver logSql = true url = "mysql://***:***@127.0.0.1:3306/emil?serverTimezone=CET&verifyServerCertificate=false&useSSL=false" } }
问题分析与解决方案
这个错误的核心原因是Ebean不支持跨不同数据库的实体关联映射。你把Company和Project分别注册到了kuno和emil两个独立的Ebean服务器(对应不同数据库),而Ebean的关联注解(@OneToMany/@ManyToOne)只能用于同一个Ebean服务器内的实体——也就是同一个数据库里的表。
当你添加关联注解后,Ebean在初始化emil服务器的Project实体时,找不到属于kuno服务器的Company实体,所以抛出了“Is class registered?”的错误。
你有两个可选的解决方向:
方案1:放弃跨库关联,手动维护关系
移除实体类上的@OneToMany和@ManyToOne注解,手动处理两者的关联逻辑:
- 在
Project里保留Long companyId字段(代替Company对象),存储关联的公司ID - 查询项目对应的公司时,手动调用
Company.find.byId(project.companyId) - 查询公司的所有项目时,手动调用
Project.find.query().where().eq("companyId", company.id).findList()
方案2:将两个实体放到同一个数据库/Ebean服务器
如果业务允许,把Company和Project调整到同一个数据库(比如都放到kuno或者emil库),然后修改ebean配置,把两个实体包注册到同一个服务器下:
ebean { kuno = ["models.kuno.*", "models.emil.*"] default = ["models.changer.*"] }
同时调整对应的数据库连接配置,确保实体都映射到同一个库的表。
补充说明
Play的sbt-play-ebean插件基于Ebean ORM,而Ebean本身设计上就不支持跨不同数据源的实体关联——因为关联查询需要数据库层面的JOIN,而跨数据库的JOIN在多数关系型数据库里本身就不支持(除非是分布式数据库的特殊实现),所以ORM层面也不会提供这种功能。
内容的提问来源于stack exchange,提问作者Jörg Bergner

