Revisé blogs, tutoriales, repositorios de la gente, pero nada ayuda. Aquí es lo que tengo:
Hay dos recipientes acoplables con bases de datos MySQL: ebooks
y sec
. Ambos contenedores están en alza, las bases de datos son visibles, puedo consultar las tablas.
Quiero tener dos fuentes de datos en mi proyecto: una para los libros electrónicos y otro para tablas Spring Security.
Escribí un sencillo CommandLineRunner
en el que acabo de Autowire ambos repositorios y comprobar sus tamaños.
Cuando ejecuto mi aplicación, me sale:
Caused by: java.sql.SQLSyntaxErrorException: Table 'ebooks.Book' doesn't exist
Pero si me quedo esto sin una segunda fuente de datos y el uso de la configuración automática de la primavera regular, libros de mesa es "vista" y que se pueden consultar.
Así que aquí está mi application.properties
:
book.datasource.url=jdbc:mysql://172.17.0.2:3306/ebooks
book.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
book.datasource.username=someuser
book.datasource.password=somepass
security.datasource.url=jdbc:mysql://172.17.0.3:3306/sec
security.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
security.datasource.username=someuser
security.datasource.password=somepass
Mis clases de entidad son pequeñas:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private String author;
private int locations;
public Book() {
}
public Book(String title, String author, int locations) {
this.title = title;
this.author = author;
this.locations = locations;
}
public Book(int id, String title, String author, int locations) {
this(title, author, locations);
this.id = id;
}
// ... getters setters and so on
}
@Entity
public class Role {
@Id
@GeneratedValue
private int id;
private String roleName;
public Role() {
}
public Role(int id, String roleName) {
this.id = id;
this.roleName = roleName;
}
// ... getters and setters
}
Estas clases están en diferentes paquetes.
Repositorios, de nuevo, nada especial:
@Repository
public interface RoleRepository extends JpaRepository<Role, Integer> {
}
Lo mismo ocurre con los libros, así que no se pegue.
Y aquí están las clases de configuración:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "rnd.mate00.twodatasources.model1",
entityManagerFactoryRef = "bookEntityManagerFactory",
transactionManagerRef = "bookTransactionManager")
public class BookDatasourceConfiguration {
@Value("${book.datasource.driver-class-name}")
private String driver;
@Value("${book.datasource.url}")
private String url;
@Value("${book.datasource.username}")
private String user;
@Value("${book.datasource.password}")
private String pass;
@Bean
@Primary
public DataSource bookDataSource() {
System.out.println("Configuring book.datasources");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(pass);
return dataSource;
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean bookEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(bookDataSource())
.packages("rnd.mate00.twodatasources.model1")
.persistenceUnit("booksPU")
.build();
}
@Bean
@Primary
public TransactionManager bookTransactionManager(EntityManagerFactoryBuilder builder) {
JpaTransactionManager manager = new JpaTransactionManager();
manager.setDataSource(bookDataSource());
manager.setEntityManagerFactory(bookEntityManagerFactory(builder).getObject());
return manager;
}
}
Y la segunda en una clase separada:
@Configuration
@EnableJpaRepositories(
basePackageClasses = { Role.class },
entityManagerFactoryRef = "securityEntityManagerFactory",
transactionManagerRef = "securityTransactionManager"
)
public class SecurityDatasourceConfiguration {
@Value("${security.datasource.driver-class-name}")
private String driver;
@Value("${security.datasource.url}")
private String url;
@Value("${security.datasource.username}")
private String user;
@Value("${security.datasource.password}")
private String pass;
@Bean
public DataSource securityDataSource() {
System.out.println("Configuring security.datasources");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(pass);
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean securityEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(securityDataSource())
.packages(Role.class)
.persistenceUnit("securityPU")
.build();
}
@Bean
public TransactionManager securityTransactionManager(EntityManagerFactoryBuilder builder) {
JpaTransactionManager manager = new JpaTransactionManager();
manager.setDataSource(securityDataSource());
manager.setEntityManagerFactory(securityEntityManagerFactory(builder).getObject());
return manager;
}
}
Clase punto de entrada no tiene anotaciones excepto @SpringBootApplication
.
Aquí está build.gradle
:
plugins {
id 'org.springframework.boot' version '2.2.0.M4'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'rnd.mate00'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
runtime('com.h2database:h2')
compile('mysql:mysql-connector-java')
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
exclude group: 'junit', module: 'junit'
}
}
test {
useJUnitPlatform()
}
Oh mi ... Lo que faltaba había una @Entity
anotación con el nombre de la tabla. Entonces:
@Entity(name = "book")
// ... and
@Entity(name = "role")
más adecuada @Column
anotación con los nombres de las columnas correspondientes. Estoy adjuntando un enlace a mi pequeña repo donde pongo un ejemplo de trabajo: https://github.com/mate0021/two_datasources.git