How to connect to two databases using spring boot application? - java

I have to connect to two oracle databases from a spring boot application. Below is what I have done so far:
application.properties
#############################################
## Database Configuration
#############################################
# HikariCP settings
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.poolName=HikariPoolOrcl
# JPA settings
spring.jpa.database=default
spring.datasource.dialect=org.hibernate.dialect.OracleDialect
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.OracleDialect
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
############################################
# OracleDB connection settings
###########################################
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.jdbc-url=jdbc:oracle:thin:#localhost:1521:orcl
spring.datasource.url=jdbc:oracle:thin:#localhost:1521:orcl
spring.datasource.username=userdev
spring.datasource.password=pass123
spring.datasource.pool-size=30
############################################
# OracleDB connection settings FOR IL DB
###########################################
spring.il.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.il.datasource.jdbc-url=jdbc:oracle:thin:#//192.126.98.77:1521/apimdbuat
spring.il.datasource.username=userdev
spring.il.datasource.password=Ahjhj20
spring.il.datasource.pool-size=30
the user model in the first database
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "User")
#Table(name = "users")
public class User {
#Id
#SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence"
)
#Column(nullable = false, updatable = false)
private Long id;
#Column(nullable = false, length = 64)
#NotBlank(message = "Firstname is required")
private String firstname;
#NotBlank(message = "Lastname is required")
#Column(nullable = false, length = 64)
private String lastname;
#NotBlank(message = "Username is required")
#Column(nullable = false, length = 64, unique = true)
private String username;
#Column(nullable = false, length = 64, unique = true)
#Email
#NotEmpty(message = "Email is required")
private String email;
#NotBlank(message = "Password is required")
#Column(nullable = false, length = 64)
#JsonIgnore
private String password;
private String profileImgUrl;
private Date lastLoginDate;
private Date joinDate;
#JsonProperty("isActive")
private boolean isActive;
#JsonProperty("isNotLocked")
private boolean isNotLocked;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "user_roles", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Role> roles = new HashSet<>();
and here is the model for the second database
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(
name = "app_config"
)
public class AppConfig {
#Id
#Column(
name = "ID"
)
#GeneratedValue(
strategy = GenerationType.AUTO
)
private Long id;
private String appCode;
private String appName;
private String version;
}
Repositories:
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByEmail(String email);
boolean existsByUsername(String username);
}
public interface AppConfigRepository extends JpaRepository<AppConfig, Long> {
}
and finally the configuration classes
#Configuration(proxyBeanMethods = false)
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager",
basePackages = {"com.app.models",
"com.app.repositories"}
)
public class PrimaryDatabaseConfig {
#Bean(name = "primaryDataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
#Primary
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("primaryDataSource") DataSource primaryDataSource) {
return builder
.dataSource(primaryDataSource)
.packages("com.app.repositories", "com.app.models")
.build();
}
#Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
#Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {
return new JpaTransactionManager(primaryEntityManagerFactory);
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager",
basePackages = {"com.app.il_models",
"com.app.il_repositories"}
)
public class SecondaryDatabaseConfig {
#Bean(name = "secondaryDataSource")
#ConfigurationProperties(prefix = "spring.il.datasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
return builder
.dataSource(secondaryDataSource)
.packages("com.app.il_models",
"com.app.il_repositories")
.build();
}
#Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
#Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
return new JpaTransactionManager(secondaryEntityManagerFactory);
}
}
The first time I got exceptions related to jdbc url and after fixing it the application started normally but when I call the api of login which take username/password I get the following error
SQL Error: 904, SQLState: 42000
ORA-00904: "USER0_"."PROFILEIMGURL": invalid identifier
Unauthorized error: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
{
"code": "401",
"message": "could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet"
}
PS: Everything worked fine when it was one database.
Thank you.

You need to add two distinct datasources in the application.properties file. like this :
spring.datasource.url= jdbc:mysql://<host1>:<port>/<database1>
spring.datasource.username= <username1>
spring.datasource.password= <password1>
spring.second-datasource.url= jdbc:mysql://<host2>:<port>/<database2>
spring.second-datasource.username= <username2>
spring.second-datasource.password= <password2>
Then, create two separate EntityManagerFactoryBeans, like this .
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityScanPackage entitiesScanPackage,
DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setPackagesToScan(entitiesScanPackage.getPackageName());
emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emf.setJpaProperties(jpaProperties());
return emf;
}
#Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(
EntitiesScanPackage entitiesScanPackage,
#Qualifier("secondDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setPackagesToScan(entitiesScanPackage.getPackageName());
emf.setJpaVendorAdapter(new HibernateJpaVendor
Thank you.

For some reasons, after connecting to two databases at the same time, spring boot models attributes names does not match the names of columns in the database although it works fine when it was only one database.
To resolve that I had to annotate all attrbutes with #Column and force the fields names as they are in the database, I also forced the table names with #Table
#Column(name = "JOIN_DATE")
private Date joinDate;
#Table(name = "USERS")

Related

Spring AuditorAware with custom User Entity

i am currently adding Auditing to my Entities. That is working fine but i want to save the Id of the User which is related to the Entity inside the #CreatedBy and #LastModifiedBy Columns and not the Name which i have right now.
I am also using Spring Security + JWT to get the User which is working fine.
How can i achieve this with the following Configuration?
User Entity:
#Entity(name = "User")
#Table(name = "user")
public class User extends Auditable<String> {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "UserSequence")
#GenericGenerator(
name = "UserSequence",
strategy = "com.simagdo.crmAPI.utils.SequenceIdGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = SequenceIdGenerator.INCREMENT_PARAM, value = "1"),
#org.hibernate.annotations.Parameter(name = SequenceIdGenerator.VALUE_PREFIX_PARAMETER, value = "130"),
#org.hibernate.annotations.Parameter(name = SequenceIdGenerator.NUMBER_FORMAT_PARAMETER, value = "%05d")
}
)
#Column(name = "Id")
private String id;
#Column(
name = "UserName",
length = 80,
unique = true)
private String userName;
#Column(name = "Password")
private String password;
}
AuditorAware:
public class AuditAwareImpl implements AuditorAware<String> {
#Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Simagdo");
}
}
The config:
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfig {
#Bean
public AuditorAware<String> auditorAware() {
return new AuditAwareImpl();
}
}
My Auditable Class:
#MappedSuperclass
public abstract class Auditable<U> {
#CreatedBy
#OneToOne(
orphanRemoval = true,
cascade = {
CascadeType.PERSIST,
CascadeType.REMOVE
}
)
private User createdBy;
#CreatedDate
#Column(
name = "CreatedDate",
nullable = false,
updatable = false
)
private LocalDateTime createdDate;
#LastModifiedBy
#OneToOne(
orphanRemoval = true,
cascade = {
CascadeType.PERSIST,
CascadeType.REMOVE
}
)
private User lastModifiedBy;
#LastModifiedDate
#Column(
name = "LastModifiedDate",
nullable = false
)
private LocalDateTime lastModifiedDate;
}
try adding following on top of entity class that contains #CreatedBy & #LastModifiedBy:
#EntityListeners({AuditingEntityListener.class})

JPA #PreUpdate #Persist seems not working as expected

I have a problem for filling auditing fields by using #PreUpdate and #PrePersist. For instance When I'd like to update a client entity, the field updatedBy and updatedAt are still null; despite when I debug, the code of preUpdate() which is annotated with #PreUpdate is executed.
Below the code of AuditingField which is responsible for creating/updating the auditing fields in each JPA entity:
#Embeddable
#Getter
#Setter
#NoArgsConstructor
public class FieldAuditing implements Serializable {
#Column(name = "DATE_CREATION", updatable = false)
private Instant createdAt;
#Column(name = "DATE_MODIFICATION", updatable = false)
private Instant updatedAt;
#Column(name = "DATE_SUPRESSION", updatable = false)
private Instant deletedAt;
#Column(name = "AUTEUR_CREATION", updatable = false, length = 100)
private String createdBy;
#Column(name = "AUTEUR_MODIFICATION", updatable = false, length = 100)
private String updatedBy;
#Column(name = "AUTEUR_SUPRESSION", updatable = false, length = 100)
private String deletedBy;
#Column(name = "IS_SUPPRIMER", nullable = false, updatable = false)
private boolean isDeleted;
#PrePersist
public void prePersist() {
setCreatedAt(Instant.now());
setCreatedBy(LoggedInUser.get());
}
#PreUpdate
public void preUpdate() {
setUpdatedAt(Instant.now());
setUpdatedBy(LoggedInUser.get());
}
#PreRemove
public void preRemove() {
setDeletedAt(Instant.now());
setDeleted(true);
setDeletedBy(LoggedInUser.get());
}
}
The client entity that contains embedded auditing fields:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(name="TF_CLIENT", schema="dbo")
public class Client implements Serializable {
private static final long serialVersionUID = 8832848102370267801L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator="native")
#GenericGenerator(name = "native", strategy = "native")
#Column(name = "CLT_ID", nullable = false)
private Long id;
#Column(name = "CLT_LIBELLE", nullable = false, length = 50, unique = true)
private String libelle;
#Temporal(TemporalType.DATE)
#Column(name = "CLT_DT_OUVERTURE", nullable = false)
private Date dateOuverture;
#Temporal(TemporalType.DATE)
#Column(name = "CLT_DT_FERMETURE")
private Date dateFermeture;
#Column(name = "CLT_B_ACTIF")
private boolean isActif;
#Embedded
private FieldAuditing fieldAuditing = new FieldAuditing() ;
//... rest of another attributes
}
The method that updates the client entity
private ClientDto save(ClientDto clientDto, Client client) {
startDateShouldBeBeforeEndDate(clientDto);
hasUniqueCodePaies(clientDto.getCodePaies());
Client clientSaved = clientRepository.save(clientMapper.toEntity(clientDto, client));
clientMapper.addOrRemoveClientActions(clientDto, clientSaved);
clientMapper.addOrRemoveClientEtats(clientDto, clientSaved);
clientRepository.save(clientSaved);
clientDto.setId(clientSaved.getId());
return clientDto;
}
And finally the persistence context configuration:
#Configuration
#PropertySource({"classpath:application.yml"})
#EnableJpaRepositories(
basePackages = "com.github.maaoutir.clientManager",
entityManagerFactoryRef = "mainEntityManager")
public class PersistenceContext {
private final Environment env;
public PersistenceContext(Environment env) {
this.env = env;
}
#Bean
#Primary
public DataSource mainDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Objects.requireNonNull(env.getProperty("spring.datasource.driverClassName")));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean mainEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mainDataSource());
em.setPackagesToScan("com.github.maaoutir.clientManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
// properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(mainEntityManager().getObject());
return transactionManager;
}
}
I'm thankful for any help.
You are using updatable=false on those columns:
#Column(name = "DATE_MODIFICATION", updatable = false)
private Instant updatedAt;
#Column(name = "AUTEUR_MODIFICATION", updatable = false, length = 100)
private String updatedBy;
This means that JPA doesn't use this field to update the column. From the JPA spec for updatable:
Whether the column is included in SQL
UPDATE statements generated by the persistence
provider.
This make sense for the createdBy or createdAt columns, that are set on #PrePersist and persisted with the first INSERT, and you don't want them to be modified afterwards. But columns updated in #PreUpdate (or #PreRemove) wont be updated with the UPDATE statement if updatable is set to false
Check if you have #EnableJpaAuditing on your configuration, and #EntityListeners(AuditingEntityListener.class) on your entity class.
Create a constructor in the class #Embeddable with super.
It worked for me.
public FieldAuditing(){
super();
}

hibernate read different relation table name after changing to multiple data sources

I am working on a project using spring boot with version 1.3.5.RELEASE, it is using spring data jpa as persistent layer. I got a task to implement multiple datasource on this project.
After implemented the code we got the error, regarding these two entity class :
#Entity
#Table(name = "TBL_MERCHANT")
public class Merchant implements Serializable {
#Id
#Column(name = "ID", length = 14, precision = 14, nullable = false)
private BigInteger id;
#Column(name = "NAME", length = 100, nullable = false)
private String name;
#JoinColumn(name = "CATEGORY_ID")
#ManyToOne(fetch = FetchType.LAZY)
private Categorization categorization;
#OrderBy("id ASC")
#ManyToMany(mappedBy = "merchants", fetch = FetchType.LAZY)
private Set<PaymentMethod> paymentMethods = new HashSet<>();
#JoinColumn(name = "USER_ID")
#OneToOne(fetch = FetchType.LAZY)
private WalletUser user;
// getter setter
and
#Entity
#Table(name = "TBL_PAYMENT_METHOD")
public class PaymentMethod implements Serializable {
#Id
#Column(name = "ID", length = 24)
private String id;
#Column(name = "DESCRIPTION", length = 255, nullable = true)
private String desc;
#ManyToMany
private Set<Merchant> merchants = new HashSet<>();
// getter setter
}
previously, the relationship between these two entities generate a table named tbl_payment_method_merchants
but after the changes this kind of error occurred
ERROR: relation "tbl_payment_method_tbl_merchant" does not exist
the following is my changes :
Before
#EnableAsync
#EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.class})
#EntityScan("com.somepackage")
#ComponentScan({"com.somepackage})
#EnableJpaRepositories("com.somepackage")
#PropertySource(value = "classpath:stringMessage.properties")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
i separate it into two different data sources,as followings :
After :
first datasource :
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = RepositoryMetadataProvider.REPOSITORY_BASE_PACKAGE,
includeFilters = #ComponentScan.Filter(ReadOnlyConnection.class),
entityManagerFactoryRef = ReadOnlyRepositoryConfig.READ_ONLY_ENTITY_MANAGER_FACTORY,
transactionManagerRef = ReadOnlyRepositoryConfig.READ_ONLY_TRX_MANAGER
)
public class ReadOnlyRepositoryConfig {
public static final String READ_ONLY_TRX_MANAGER ="readOnlyTransactionManager";
public static final String READ_ONLY_ENTITY_MANAGER ="readOnlyEntityManager";
public static final String READ_ONLY_ENTITY_MANAGER_FACTORY ="readOnlyEntityManagerFactory";
#Bean(name = READ_ONLY_TRX_MANAGER)
PlatformTransactionManager readOnlyTransactionManager(
#Qualifier(READ_ONLY_ENTITY_MANAGER_FACTORY) EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Bean(name = READ_ONLY_ENTITY_MANAGER)
EntityManager readOnlyEntityManager(#Qualifier(READ_ONLY_ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean masterEntityFactory) {
return masterEntityFactory.getObject().createEntityManager();
}
#Singleton
#Bean(name = READ_ONLY_ENTITY_MANAGER_FACTORY)
LocalContainerEntityManagerFactoryBean readOnlyEntityFactory(
EntityManagerFactoryBuilder builder,
#Qualifier(DataSourceConfig.WALLET_SLAVE_DATASOURCE) DataSource dataSource) {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = builder
.dataSource(dataSource)
.packages(RepositoryMetadataProvider.REPOSITORY_BASE_PACKAGE)
.build();
localContainerEntityManagerFactoryBean.setMappingResources(new String[]{"META-INF/orm.xml", "META-INF/bv-orm.xml", "META-INF/infinitium-orm.xml", "META-INF/cam-orm.xml"});
return localContainerEntityManagerFactoryBean;
}
}
second datasource :
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = RepositoryMetadataProvider.REPOSITORY_BASE_PACKAGE,
excludeFilters =#ComponentScan.Filter(ReadOnlyConnection.class),
entityManagerFactoryRef = TransactionalRepositoryConfig.TRANSACTIONAL_ENTITY_MANAGER_FACTORY,
transactionManagerRef = TransactionalRepositoryConfig.TRANSACTIONAL_TRX_MANAGER
)
public class TransactionalRepositoryConfig {
public static final String TRANSACTIONAL_TRX_MANAGER ="transactionalTransactionManager";
public static final String TRANSACTIONAL_ENTITY_MANAGER ="transactionalEntityManager";
public static final String TRANSACTIONAL_ENTITY_MANAGER_FACTORY ="transactionalEntityManagerFactory";
#Primary
#Bean(name = TRANSACTIONAL_TRX_MANAGER)
PlatformTransactionManager masterTransactionManager(
#Qualifier(TRANSACTIONAL_ENTITY_MANAGER_FACTORY) EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Primary
#Bean(name = TRANSACTIONAL_ENTITY_MANAGER)
EntityManager masterEntityManager(#Qualifier(TRANSACTIONAL_ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean masterEntityFactory){
return masterEntityFactory.getObject().createEntityManager();
}
#Singleton
#Primary
#Bean(name = TRANSACTIONAL_ENTITY_MANAGER_FACTORY)
LocalContainerEntityManagerFactoryBean masterEntityFactory(
EntityManagerFactoryBuilder builder,
#Qualifier(DataSourceConfig.WALLET_MASTER_DATASOURCE) DataSource dataSource) {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = builder
.dataSource(dataSource)
.packages(RepositoryMetadataProvider.REPOSITORY_BASE_PACKAGE)
.build();
localContainerEntityManagerFactoryBean.setMappingResources(new String[]{"META-INF/orm.xml", "META-INF/bv-orm.xml", "META-INF/infinitium-orm.xml", "META-INF/cam-orm.xml"});
return localContainerEntityManagerFactoryBean;
}
}
org.hibernate.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "tbl_payment_method_tbl_merchant" does not exist, when using multiple datasource
#Entity
#Table(name = "TBL_MERCHANT")
public class Merchant implements Serializable {
#OrderBy("id ASC")
#ManyToMany(mappedBy = "merchants", fetch = FetchType.LAZY)
#JoinTable(name = "tbl_payment_method_merchants",
joinColumns = #JoinColumn(name = "idMerchant", referencedColumnName = "idMerchant"),
inverseJoinColumns = #JoinColumn(name = "idPaymentMethod", referencedColumnName = "idPaymentMethod"))
private Set<PaymentMethod> paymentMethods = new HashSet<>();
// getter setter
#Entity
#Table(name = "TBL_PAYMENT_METHOD")
public class PaymentMethod implements Serializable {
#ManyToMany
#JoinTable(name = "tbl_payment_method_merchants",
joinColumns = #JoinColumn(name = "idPaymentMethod", referencedColumnName = "idPaymentMethod"),
inverseJoinColumns = #JoinColumn(name = "idMerchant", referencedColumnName = "idMerchant"))
private Set<Merchant> merchants = new HashSet<>();
// getter setter
}
Can you try above. This will ensure that the metadata for the many-to-many relationship is available on both the entities.
I also suggest replacing FetchType.EAGER with FetchType.LAZY.

javax.persistence.EntityNotFoundException while querying AuditEntity when record exists in database

I am currently using Spring Boot 2.1.1.RELEASE, Hibernate Envers 5.3.7.Final.
When I query for my AuditEntity for my User class, it will throw the javax.persistence.EntityNotFoundException saying my User record does not exist. However, in database it does exist. Anyone have any idea why?
User Entity
#Entity
#EntityListeners(AuditingEntityListener.class)
#Audited
#Table(name = "[user]")
#Getter
#Setter
#NoArgsConstructor
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, unique = true)
#Length(max = 20)
#Unique
private String username;
#Column(nullable = false)
private String password;
#Column
private String email;
#Column(nullable = false)
private String name;
#Audited(targetAuditMode = NOT_AUDITED)
#ManyToOne(fetch = FetchType.LAZY)
#CreatedBy
private User createByUser;
#CreatedDate
private LocalDateTime createDatetime;
#Audited(targetAuditMode = NOT_AUDITED)
#ManyToOne(fetch = FetchType.LAZY)
#LastModifiedBy
private User updateByUser;
#LastModifiedDate
private LocalDateTime updateDatetime;
#Column
private Boolean active = true;
#Column
private Boolean deleted = false;
}
While querying for the audit entity, the property createByUser updateByUser throws the exception javax.persistence.EntityNotFoundException: Unable to find data.entity.User with id 2. However, in the database I have this record.
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery().forRevisionsOfEntity(User.class, true, true);
query.add(AuditEntity.id().eq(id));
return query.getResultList();
EDITED on 2018-12-05
The problem seems to be the problem with this id 2 record, I insert this records from backend and not via the frontend which will generate a record in _aud table. Not sure how does this auditing logic works.
I figured out that it is indeed due to missing records in the _aud table.
Instead of inserting test records directly to backend. I decided to create a routine in the Application class of the spring boot.
#SpringBootApplication(
exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
#EntityScan(basePackageClasses = {WebApplication.class})
#EnableAsync
#EnableCaching
#EnableTransactionManagement
#EnableConfigurationProperties
public class WebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
#Bean
public CommandLineRunner setUpInitialData(
EntityRepository1 entityRepository1,
EntityRepository2 entityRepository2) {
return args -> {
// do all data insertion here...
};
}
}
Either this or the _aud table must also contains the corresponding entry for the entity.

Unilateral ManyToOne child, with a #Lob String field, returns a null parent

Link to Github
The parent entity:
#Entity
#Table(name="paciente")
public class Paciente {
#Id
#Column(name="id", nullable=false, updatable=false, columnDefinition = "BINARY(16)")
private UUID id;
(...)
}
And the child entity:
#Entity
#Table(name = "avaliacao_clinica")
public class AvaliacaoClinica {
#Id
#Column(name="id", nullable=false, updatable=false, columnDefinition = "BINARY(16)")
private UUID id;
#ManyToOne(optional=false)
private Paciente paciente;
#Lob
private String obs;
(..)
}
This is an unidirectional many to one relationship.
The obs field used to have no annotation but I needed it to be a Lob. After the change was made, the reference to the parent is always null.
This is the hibernate config on spring ecosystem:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
String datasourceName = null;
String debug = null;
switch (ApplicationConstrains.ENVIROMENT) {
case DEVELOPMENT:
datasourceName = "jdbc/vet_dev";
debug = "true";
break;
case PRODUCTION:
datasourceName = "jdbc/vet_production";
debug = "false";
break;
}
Properties properties = new Properties();
properties.setProperty(Environment.HBM2DDL_AUTO, "update");
properties.setProperty(Environment.STORAGE_ENGINE, "innodb");
properties.setProperty(Environment.JDBC_TIME_ZONE, "UTC");
properties.setProperty(Environment.DIALECT, "org.hibernate.dialect.MySQL57Dialect");
properties.setProperty(Environment.SHOW_SQL, debug);
properties.setProperty(Environment.FORMAT_SQL, debug);
properties.setProperty(Environment.USE_SQL_COMMENTS, debug);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.setDataSource(new JndiDataSourceLookup().getDataSource(datasourceName));
factoryBean.setJpaProperties(properties);
factoryBean.setPackagesToScan(Paciente.class.getPackage().getName());
return factoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setValidateExistingTransaction(true);
txManager.setRollbackOnCommitFailure(true);
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}

Categories