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.
Related
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")
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();
}
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;
}
I use Hibernate and I want to filter entities with the Hibernate filter options. But the filter will be ignored. The Workspace entity include a user entity. And i want all workspace entitys with only thise user with doesent have a NULL-Value in the E-Mail field in the database.
I look forward to hearing from you. Thanks
Workspace
#Entity
#Table(name = "WORKSPACEENTITY", schema = "pwdmanager")
#PrimaryKeyJoinColumn(name = "workspace_id", referencedColumnName = "id")
#Setter
#Getter
#FilterDef(name = "FILTER")
public class DBWorkspaceEntity{
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "wkse_space")
private List<DBPasswordEntity> pwde_password;
#OneToOne(fetch = FetchType.EAGER)
#Filters({
#Filter(name="FILTER", condition="{u}.usre_email is not null", aliases={#SqlFragmentAlias(alias="u", entity=DBUserEntity.class)}),
})
private DBUserEntity usre_user;
}
User
#Entity
#Table(name = "USERENTITY", schema = "pwdmanager")
#Setter
#Getter
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "id")
public class DBUserEntity{
#Basic
#Column(name = "USRE_EMAIL", nullable = true, length = 1024)
private String usre_email;
}
Hibernate
public class MasterDao{
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
public void prepare() {
sessionFactory = HibernateUtil.getSessionFactory();
this.session = sessionFactory.openSession();
}
public void filter(final DBUserEntity usr) {
try {
session.enableFilter("FILTER");
Query query = session.createQuery("FROM " + DBWorkspaceEntity.class.getSimpleName());
List<DBWorkspaceEntity> result = query.list();
session.disableFilter("FILTER_USER");
} catch (final HibernateException ex) {
log.error(ex);
}
}
}
I think, usre_email in filter condition should be column name.
USRE_EMAIL
I got a common exception "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" in spite of "fetch = FetchType.EAGER" and I can't (wan't) manage hibernate session manually (I use Spring-Boot-starter-data-jpa).
I have a Hilder entity, that contains property of CommonType (TapeA or TypeB):
#Entity
public class Holder<T extends CommonType> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Any(metaColumn = #Column(name = "type", nullable = false), optional = false, fetch = FetchType.EAGER)
#Cascade(org.hibernate.annotations.CascadeType.ALL )
#AnyMetaDef(
idType = "long",
metaType = "string",
metaValues = {
#MetaValue(value = "TypeA", targetEntity = TypeB.class),
#MetaValue(value = "TypeB", targetEntity = TypeA.class)
})
#JoinColumn(name = "property_id", nullable = false)
private T type;
//getters and setters}
TypeB looks like TypeA:
#Entity
public class TypeA implements CommonType {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private double param=0;
//getters and setters
}
And repository for Holder :
import org.springframework.transaction.annotation.Transactional;
#Transactional
public interface HolderRepository extends CrudRepository<Holder, Long> {
}
Runner:
#Configuration
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class);
HolderRepository repository = context.getBean(HolderRepository.class);
TypeA simpleDeviceState = new TypeA();
Holder<TypeA> holder = new Holder<>(simpleDeviceState);
repository.save(holder);
Holder holder1=repository.findAll().iterator().next();
TypeA typeA= (TypeA) holder1.getType();
System.out.println("Param: "+typeA.getParam());
context.close();
}}
Pom contain org.springframework.boot::spring-boot-starter-data-jpa and com.h2database::h2 only.
In printing point exception appears. I guess that I get org.hibernate.LazyInitializationException because fetch = FetchType.EAGER does't work.
Also cascading works for PERSIST only.
Maybe the problem in mixing Hibernate and JPA, but I can not handle it.
Thanks in advance!
It is not the good solution, but it works. But I would like to get better one.
It is service with #Transactional and Hibernate.initialize:
#Component
#Transactional
public class ServiceHolder {
#Autowired
HolderRepository holderRepository;
public Holder getH(long id) {
Holder holder = holderRepository.findOne(id);
Hibernate.initialize(holder.getType());
return holder;
}
}