Spring Boot Data - Getting 'Not a managed Type' error when the package is not included in default entityManager - spring-boot

My application has two entity managers (entityManagerFactory and entityManagerFactorySec). The default is associated with package 'com.abc.model' and the second one is associated with package 'com.abc.uw.model'.
What I observed is that the application startup is fine only if I include the second package as well to the default Entitymanager. Getting the following error even though I see from the logs that the repo got created.
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dmUwRefRulesRsltRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class com.abc.uw.model.DmUwRefRulesRslt
Java configs are
entityManagerFactory:
return builder.dataSource(dmDs).packages(new String[]{"com.abc.model"}).build();
//startup is fine only with the commented line below
//return builder.dataSource(dmDs).packages(new String[]{"com.abc.model","com.abc.uw.model"}).build();
entityManagerFactorySec:
return builder.dataSource(dataSource).packages(new String[]{
"com.abc.uw.model"}).persistenceUnit("alternate").build();
Could not find why it is not working when the package is included in the packages of the second entitymanager factory
I am providing the complete snippet for both the configs.
Config 1:
#Configuration
#PropertySource("classpath:application.yml")
#EnableJpaRepositories ( basePackages = "com.abc.pcs", entityManagerFactoryRef = "entityManagerFactory")
public class PersistenceConfig {
#Bean(name = "dmDs")
#ConfigurationProperties(prefix = "dm.datasource")
public FactoryBean dmJndiDataSource() {
return new JndiObjectFactoryBean();
}
#Bean("entityBuilder")
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return new EntityManagerFactoryBuilder(hibernateJpaVendorAdapter,
new HashMap<String, Object>(){{put("eclipselink.weaving", "false");}}, null);
}
/**
* #param builder
* #param dmDs
* #return
* #link https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-use-two-entity-managers
*/
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, #Qualifier("dmDs") final DataSource dmDs) {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = builder.dataSource(dmDs).packages(new String[]{"com.abc.model"}).build();
return localContainerEntityManagerFactoryBean;
}
#Bean("transactionManager")
public PlatformTransactionManager transactionManager(#Qualifier("entityManagerFactory") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Config 2:
#Configuration
#PropertySource("classpath:application.yml")
#EnableJpaRepositories ( basePackages = {"com.abc.clp.hnw","com.abc.uw"}, entityManagerFactoryRef = "entityManagerFactorySec")
public class AltPersistenceConfig {
#Bean(name = "dsSecondary")
#ConfigurationProperties(prefix = "ds_secondary.datasource")
public FactoryBean dmEclipseLinkJndiDataSource() {
return new JndiObjectFactoryBean();
}
#Bean(name="entityManagerFactorySec")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySec( EntityManagerFactoryBuilder builder, #Qualifier("dsSecondary") final DataSource dataSource) {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = builder.dataSource(dataSource).packages(new String[]{
"com.abc.uw.model"}).persistenceUnit("alternate").build();
return localContainerEntityManagerFactoryBean;
}
#Bean("transactionManagerSec")
public PlatformTransactionManager transactionManagerEclipseLink(#Qualifier("entityManagerFactorySec") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}

I'm 99% sure that one way or the other you are referencing entities from the second package in the repository for the first one. Either directly or possibly through some attribute of an entity.

Related

Springboot JPA javax.persistence.TransactionRequiredException: Executing an update/delete query

I use Springboot JPA to create multiple configuration with multiple dataSource. I created one configuration for Oracle database like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "mypackages.models.another",
entityManagerFactoryRef = "anotherEntityManagerFactory",
transactionManagerRef = "anotherTransactionManager"
)
public class AnotherJpaConfiguration {
#Qualifier("anotherDataSource")
#Autowired
DataSource dataSource;
#Bean(name = "anotherEntityManager")
public EntityManager entityManager() {
return anotherEntityManagerFactory().createEntityManager();
}
#Bean(name = "anotherEntityManagerFactory")
public EntityManagerFactory anotherEntityManagerFactory() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setJpaVendorAdapter(vendorAdapter);
emf.setPackagesToScan(new String[] { "mypackages.models.destination" }); // <- package for entities
emf.setPersistenceUnitName("anotherPersistenceUnit");
emf.setJpaProperties(properties);
emf.afterPropertiesSet();
return emf.getObject();
}
#Bean(name = "anotherTransactionManager")
public PlatformTransactionManager anotherTransactionManager() {
return new JpaTransactionManager(anotherEntityManagerFactory());
}
}
I used the entityManager to execute a native query:
#Service
#Slf4j
public class DestinationServiceImpl extends DestinationService{
#Qualifier("anotherEntityManager")
#Autowired
public EntityManager em2;
#Transactional("anotherTransactionManager")
#Override
void saveMtdoDictRefrQuery() {
joinTransaction();
String query = "MY INSERT QUERY)";
List<Object> result2 = this.em2.createNativeQuery("MY SELECT QUERY").getResultList();
log.debug(String.valueOf(result2.size())); // it's work
this.em2.createNativeQuery(query).executeUpdate(); // doesn't work
}
}
But I have this error:
javax.persistence.TransactionRequiredException: Executing an update/delete query
Any idea?
Thank you

Spring Boot, required a single bean, but 2 were found when creating multiple datasources

I have an application in Spring Boot 1.4 which I'm trying to add additional datasources to.
First I setup a primary datasource and ran the application to check it still worked, and it did. Then I went ahead and added a second datasource, but when I did that I got the following error;
Description:
Field userRepo in com.nationallocums.config.CustomUserDetailsService required a single bean, but 2 were found:
- nlDoctorsEntityManager: defined by method 'nlDoctorsEntityManager' in class path resource [com/nationallocums/config/NLDoctorsDataSourceConfiguration.class]
- primaryEntityManager: defined by method 'primaryEntityManager' in class path resource [com/nationallocums/config/PrimaryDataSourceConfiguration.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
I don't understand why I'm seeing this error, as I've clearly marked one of the datasources with #Primary, but it seems Spring Boot isn't picking that up.
Here's my two datasource configurations;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager",
basePackages = { "com.nationallocums.repository" })
#EnableTransactionManagement
public class PrimaryDataSourceConfiguration {
#Bean(name = "primaryDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("primaryDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.model")
.persistenceUnit("primary")
.build();
}
#Bean(name = "primaryTransactionManager")
#Primary
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("primaryEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
and...
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "nlDoctorsEntityManager",
transactionManagerRef = "nlDoctorsTransactionManager",
basePackages = { "com.nationallocums.eclipse.nldoctorsrepository" })
#EnableTransactionManagement
public class NLDoctorsDataSourceConfiguration {
#Bean(name = "nlDoctorsDataSource")
#ConfigurationProperties(prefix = "spring.nldoctors-datasource")
public DataSource nlDoctorsDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nlDoctorsEntityManager")
public LocalContainerEntityManagerFactoryBean nlDoctorsEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("nlDoctorsDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.eclipse.model")
.persistenceUnit("nlDoctors")
.build();
}
#Bean(name = "nlDoctorsTransactionManager")
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("nlDoctorsEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Can anyone spot what I've done wrong?
I managed to fix this by changing my repository from...
#PersistenceContext
private EntityManager entityManager;
to...
#Autowired
#Qualifier("primaryDataSource")
private EntityManager entityManager;

No Entity Manager with actual transaction available for current thread - cannot reliably process 'persist' call

From service layer which is annotated as Transactional; another service of an external jar is called which is interacting with DB using entity manager(autowired) .
But I am getting this error that "No entity manager with actual transaction available for current thread".
Later I tried to create entity manager manually via EntityManager.getEntityManagerFactory().createEntityManager() which creates the entity manager and then I needed to manually start the transaction also.
My query is : 1. why transaction is not getting propagated to service of external jar.
2. Why autowired EntityManager in external jar is giving error of "No entity manager with actual transaction available for current thread".
General configuration which I provided is:
Appconfig class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "base.package.*" })
#PropertySource("classpath:application-development.properties")
public class AppConfig implements WebMvcConfigurer{}
JNDI config class:
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:persistence-jndi.properties" })
#EnableJpaRepositories(basePackages = "base.package.*", entityManagerFactoryRef = "em", transactionManagerRef = "tm")
public class PersistenceConfig {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean em() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds());
em.setPackagesToScan(new String[] {"base.package"});
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
final HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public DataSource ds() {
try {
return (DataSource) new JndiTemplate().lookup(env.getProperty("jdbc.url"));
} catch (NamingException e) {
return null;
}
}
#Bean
#Primary
public PlatformTransactionManager tm() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(em().getObject());
return transactionManager;
}
}
After this setup, I gave #transactional spring annotation at my application's service class which calls the StorageImpl class defined in external jar which is like:
#Repository
public class StorageImpl implements StorageInterface {
#PersistenceContext(unitName = "default")
private EntityManager em;
public saveData(){
em.persist(somedata); // here it is giving error like No Entity Manager with actual transaction available for current thread - cannot reliably process 'persist' call
}
}

Couldn't determine Dialect for "oracle"

I am using Spring boot(2.3.5), Oracle19c DB, and Hibernate(5.4).
I tried to make multi-datasource connection, but I keep getting a dialect error Couldn't determine Dialect for "oracle".
Error creating bean with name 'jdbcDialect' defined in class path resource [org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration$SpringBootJdbcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.relational.core.dialect.Dialect]: Factory method 'jdbcDialect' threw exception; nested exception is org.springframework.data.jdbc.repository.config.DialectResolver$NoDialectException: Cannot determine a dialect for org.springframework.jdbc.core.JdbcTemplate#2ba9ed19. Please provide a Dialect.
I basically followed this tutorial to configure multiple data sources.
application.properties:
spring.datasource-primary.username=oracleprimary
spring.datasource-primary.password=oracleprimary
spring.datasource-primary.url=jdbc:oracle:thin:#//localhost:1521/orcl
spring.datasource-secondary.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource-secondary.username=oraclesecondary
spring.datasource-secondary.password=oraclesecondary
spring.datasource-secondary.url=jdbc:oracle:thin:#//localhost:1521/orcl
Primary configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager",
basePackages = {"com.foo.primary.repository"})
public class PrimaryDataSourceConfiguration {
#Primary
#Bean(name = "primaryDataSourceProperties")
#ConfigurationProperties("spring.datasource-primary")
public DataSourceProperties primaryDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name = "primaryDataSource")
#ConfigurationProperties("spring.datasource-primary.configuration")
public DataSource primaryDataSource(#Qualifier("primaryDataSourceProperties") DataSourceProperties primaryDataSourceProperties) {
return primaryDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Primary
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder primaryEntityManagerFactoryBuilder, #Qualifier("primaryDataSource") DataSource primaryDataSource) {
Map<String, String> primaryJpaProperties = new HashMap<>();
primaryJpaProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
return primaryEntityManagerFactoryBuilder
.dataSource(primaryDataSource)
.packages("com.foo.primary.model")
.persistenceUnit("primaryDataSource")
.properties(primaryJpaProperties)
.build();
}
#Primary
#Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
#Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {
return new JpaTransactionManager(primaryEntityManagerFactory);
}
Second configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager",
basePackages = {"com.foo.secondary.repository"})
public class SecondaryDataSourceConfiguration {
#Bean(name = "secondaryDataSourceProperties")
#ConfigurationProperties("spring.datasource-secondary")
public DataSourceProperties secondaryDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "secondaryDataSource")
#ConfigurationProperties("spring.datasource-secondary.configuration")
public DataSource secondaryDataSource(#Qualifier("secondaryDataSourceProperties") DataSourceProperties secondaryDataSourceProperties) {
return secondaryDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder secondaryEntityManagerFactoryBuilder, #Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
Map<String, String> secondaryJpaProperties = new HashMap<>();
secondaryJpaProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
return secondaryEntityManagerFactoryBuilder
.dataSource(secondaryDataSource)
.packages("com.foo.secondary.model")
.persistenceUnit("secondaryDataSource")
.properties(secondaryJpaProperties)
.build();
}
#Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
#Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
return new JpaTransactionManager(secondaryEntityManagerFactory);
}
}
I also tried org.hibernate.dialect.Oracle10gDialect, and set spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect in application.properties, but nothing changed.
How can I properly configure dialect for oracle?
Spring Data JDBC does not support oracle dialect. You need to define your dialect that implements JdbcDialectProvider.
public final class OracleDialect implements DialectResolver.JdbcDialectProvider {
private static Dialect getDialect(Connection connection) throws SQLException {
DatabaseMetaData metaData = connection.getMetaData();
String name = metaData.getDatabaseProductName().toLowerCase(Locale.ROOT);
if (name.contains("oracle")) {
return AnsiDialect.INSTANCE;
}
return null;
}
#Override
public Optional<Dialect> getDialect(JdbcOperations operations) {
return Optional.ofNullable(operations.execute((ConnectionCallback<Dialect>) OracleDialect::getDialect));
}
}
Add spring-boot-starter-data-jdbc dependency in your build.gradle or pom.xml.
Then, as mentioned in the blog, create spring.factories file in resources/META-INF, and paste the following command:
org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=<your-package>.OracleDialect
Also, since both databases you use are the same (OracleDB), you do not need to set .properties() for entity manager. As #SternK mentioned, you can have spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect in your application.properties only.

Spring Data with JPA not rolling back transaction on error

We have Spring Data configured for JPA. A Service Transaction method doesn't get rolled back for an error (e.g. a DB ConstraintViolationException).
The closest I could find was this
(Transaction not rolling back) Spring-data, JTA, JPA, Wildfly10
but we don't have any XML configuration, all of our configuration is Java-based.
Essentially, a service method looks like this: no errors are caught, everything thrown.
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
public void insertEvent() throws Exception {
// Part 1
EventsT event = new EventsT();
// populate it..
eventsDAO.save(event);
// Part 2 - ERROR HAPPENS HERE (Constraint Violation Exception)
AnswersT answer = new AnswersT();
// populate it..
answersDAO.save(answer);
}
Part 2 fails. But after the error and return, I see that the Event (Part 1) is still populated in the DB.
We also tried various combinations of #Transactional, nothing worked:
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
#Transactional(readOnly = false)
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = ConstraintViolationException.class, readOnly = false)
Spring Data CRUD DAO Interfaces:
#Repository
public interface EventsDAO extends JpaRepository<EventsT, Integer> {
}
#Repository
public interface AnswersDAO extends JpaRepository<AnswersT, Integer> {
}
JpaConfig:
#Configuration
#EnableJpaRepositories(basePackages = "com.myapp.dao")
#PropertySource({ "file:${conf.dir}/myapp/db-connection.properties" })
public class JpaConfig {
#Value("${jdbc.datasource}")
private String dataSourceName;
#Bean
public Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernate.dialect", PostgreSQL95Dialect.class.getName());
//props.put("hibernate.cache.provider_class", HashtableCacheProvider.class.getName());
return props;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() throws NamingException {
return new JpaTransactionManager( entityManagerFactory().getObject() );
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan("com.myapp.domain", "com.myapp.dao");
return lef;
}
#Bean
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(dataSourceName);
}
}
Have there been any transaction rollback issues with Spring Data & JPA?
Believe it or not we fixed it. There were 2 parts to the solution:
1) Add #EnableTransactionManagement to JpaConfig as ledniov described, but that alone wasn't enough;
2) Also in JpaConfig in entityManagerFactory(), add the Service class package to the following setPackagesToScan. Previously, the domain object package was there, but the service object package was not. We added "myapp.service", the 2nd package.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan("myapp.domain", "myapp.service"); //NOTE: Service was missing
return lef;
}
You have to add #EnableTransactionManagement annotation to JpaConfig class in order to enable Spring's annotation-driven transaction management capability.

Resources