ManyToMany(OneToMany too) doesn't work when method directly return this list without any actions with it - spring

I trying to get list of channels, but hibernate doesn't do the request to DB when I return this list directly from method.
#Transactional
public List<Channel> getChannelsOfCustomer(Customer customer){
Session session = sessionFactory.getCurrentSession();
Customer customer1 = session.get(Customer.class, customer.getCustomerId());
return customer1.getChannels();
}
Logs:
Hibernate: select customer0_.customer_id as customer1_1_0_, customer0_.name as name2_1_0_ from customer customer0_ where customer0_.customer_id=?
If I call system.out.println, then I get channels and can return this list
#Transactional
public List<Channel> getChannelsOfCustomer(Customer customer){
Session session = sessionFactory.getCurrentSession();
Customer customer1 = session.get(Customer.class, customer.getCustomerId());
List<Channel> channels = customer1.getChannels();
System.out.println(channels);
return channels;
}
Logs:
Hibernate: select customer0_.customer_id as customer1_1_0_, customer0_.name as name2_1_0_ from customer customer0_ where customer0_.customer_id=?
Hibernate: select channels0_.customer_id as customer4_2_0_, channels0_.channel_id as channel_3_2_0_, channel1_.channel_id as channel_1_0_1_, channel1_.name as name2_0_1_ from customer_channel channels0_ inner join channel channel1_ on channels0_.channel_id=channel1_.channel_id where channels0_.customer_id=?
Why Hibernate doesn't trigger on getChannels() method? Do I need to add something to configuration?
Configuration:
Customer:
#ManyToMany
#JoinTable(
name="customer_channel",
joinColumns = #JoinColumn(name="customer_id"),
inverseJoinColumns = #JoinColumn(name="channel_id")
)
private List<Channel> channels;
Channel:
#ManyToMany(mappedBy = "channels")
private List<Customer> customers;
Hibernate config:
#Configuration
#PropertySource("classpath:hibernate.properties")
#EnableTransactionManagement
public class HibernateConfig {
private final Environment environment;
#Autowired
public HibernateConfig(Environment environment) {
this.environment = environment;
}
#Bean
public DataSource postgresDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Objects.requireNonNull(environment.getProperty("hibernate.driver_class")));
dataSource.setUrl(environment.getProperty("hibernate.connection.url"));
dataSource.setUsername(environment.getProperty("hibernate.connection.username"));
dataSource.setPassword(environment.getProperty("hibernate.connection.password"));
return dataSource;
}
private Properties hibernateProperties(){
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
return properties;
}
#Bean
public LocalSessionFactoryBean sessionFactory(){
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(postgresDataSource());
sessionFactory.setPackagesToScan("test.spring.mvc.model");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager(){
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}

Related

org.springframework.orm.jpa.JpaSystemException: could not initialize proxy [Order#orderdate] - no Session;

My spring boot v2.5.5 application has master slave mysql db configured.
Primary Data Source Config
#EnableJpaRepositories(
basePackages = "com.dummy",
excludeFilters = #ComponentScan.Filter(ReadOnlyRepository.class),
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
#Configuration
public class PrimaryDataSourceConfig {
#Autowired
private Environment env;
#Value("${master.jdbc.url}")
private String jdbcURL;
#Bean
#Primary
public DataSource primaryDataSource() throws Exception {
return DataSourceBuilder.create()
.driverClassName("com.mysql.jdbc.Driver")
.url(jdbcURL)
.username("root")
.password("root").build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() throws Exception {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan("com.dummy");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
#Primary
public PlatformTransactionManager primaryTransactionManager() throws Exception {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(primaryEntityManagerFactory().getObject());
return transactionManager;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("primary.hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("primary.hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty( "primary.hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "false");
return hibernateProperties;
}
}
Slave Datasource Config
#Configuration
#EnableJpaRepositories(
basePackages = "com.dummy.submodule",
includeFilters = #ComponentScan.Filter(ReadOnlyRepository.class),
entityManagerFactoryRef = "readOnlyEntityManagerFactory",
transactionManagerRef = "readOnlyTransactionManager"
)
public class ReadOnlyDataSourceConfig {
#Autowired
private Environment env;
#Value("${slave.jdbc.url}")
private String jdbcURL;
#Bean
public DataSource readOnlyDataSource() throws Exception {
return DataSourceBuilder.create()
.driverClassName("com.mysql.jdbc.Driver")
.url(jdbcURL)
.username("root")
.password("root").build();
}
#Bean
public LocalContainerEntityManagerFactoryBean readOnlyEntityManagerFactory() throws Exception {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(readOnlyDataSource());
em.setPackagesToScan("com.dummy.submodule");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public PlatformTransactionManager readOnlyTransactionManager() throws Exception {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(readOnlyEntityManagerFactory().getObject());
return transactionManager;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("readonly.hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("readonly.hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty( "readonly.hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "false");
return hibernateProperties;
}
}
Entity
#Entity
#Getter
#Setter
public class Product implements Serializable {
private static final long serialVersionUID = 8135071385764991866L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#NotNull
#JoinColumn(name = "status")
private Order order
}
#Getter
#Setter
#Entity
public class Order implements Serializable {
private static final long serialVersionUID = 8135071385764991879L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date orderdate;
}
I am trying to fetch Product from read only(slave) db in my service layer
#Service
#AllArgsConstructor
public class ProductService{
private ProductRepository repo;
public void performOperation(){
Optional<Product> product = repo.findById(1l, Product.class);
if(product.isPresent()){
Order order = product.get().getOrder();
Date orderDate = order.getOrderDate(); // this line gives below exception
}
}
}
Readonly Repository:
#Repository
#ReadOnlyRepository
public interface ProductRepository extends JpaRepository<Product, Long> {
<T> Optional<T> findById(final Long id, Class<T> type);
}
Exception:
2021-10-22 12:35:11,109 ERROR [http-nio-8080-exec-8] com.dummy.logging.LoggingClass:41 Exception_Occurred::{}
org.springframework.orm.jpa.JpaSystemException: could not initialize proxy [com.dummy.submodule.entities.Order#orderdate] - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy [com.dummy.submodule.entities.Order#orderdate] - no Session
Note: The LazyInitializationException occurs only when I am fetching Product from slave db. When same operation i am trying to perform using master db I am not getting LazyInitializationException.
This issue for now i have handled by making Order as FetchType.EAGER.
But i am trying to find out reason behind this in case of master slave db and how to achieve it with LAZY fetch type.
Annotate performOperation with #Transactional like below:
#Service
#AllArgsConstructor
public class ProductService{
private ProductRepository repo;
#Transactional
public void performOperation(){
Optional<Product> product = repo.findById(1l, Product.class);
if(product.isPresent()){
Order order = product.get().getOrder();
Date orderDate = order.getOrderDate(); // this line gives below exception
}
}
}

Spring + JPA + H2 Strange behaviours of lazy fetch and bidirectional OneToMany

I have been playing for a long time with JPA, in the past through EJBs, now with Spring. I have recently noticed some weird behaviours I can hardly explain.
First the bidirectionnal OneToMany
My bidirectional OneToMany is correctly set with a mappedBy.
#Entity
public class EntityOne {
#Id #GeneratedValue
private int id;
#OneToMany(mappedBy = "one")
private Set<EntityTwo> twos;
...
#Entity
public class EntityTwo {
#Id #GeneratedValue
private int id;
#ManyToOne
private EntityOne one;
...
Then this does not update the database :
#Transactional
public void firstWay(){
EntityOne e1=em.find(EntityOne.class,1);
EntityTwo e2=em.find(EntityTwo.class,1);
e1.getTwos().add(e2);
}
while this does :
#Transactional
public void secondWay(){
EntityOne e1=em.find(EntityOne.class,1);
EntityTwo e2=em.find(EntityTwo.class,1);
e2.setOne(e1);
}
I am quite puzzled...
Then the lazy fetch :
// this is just a tool example...
public void someFindBy() {
EntityOne e1=em.find(EntityOne.class,1);
for (EntityTwo e2:e1.getTwos()) {
System.out.println(e2);
}
}
leads to LazyExceptionError... Shouldn't my "e1" entity remain attached until the end of the method and thus hibernate resolve the fetch (I use the default persistence context ,i.e. Transaction scoped. I did also try to make the method transactional by annotating it with #Transactional but that didn't change anything).
So, well, I could use an Entity Graph or a Join Fetch, but, still, I wonder why it doesn't work as is...
Here is the Spring configuration file :
#Configuration
#ComponentScan(basePackages = {"facade"})
#EnableTransactionManagement
public class ClientWebConfig extends WebMvcConfigurerAdapter {
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.H2)
.build();
return db;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.setProperty("hibernate.hbm2ddl.import_files" ,"insert-data.sql");
return properties;
}
#Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
fix this entity.
cascade, fetch must be set to update.
#Entity
public class EntityOne {
#Id #GeneratedValue
private int id;
**#OneToMany(mappedBy = "one", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)**
private Set<EntityTwo> twos;

HQL ingoring transaction

im building an Spring 4 + Hibernate 4 app. My config is this:
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(packagesDaoAnotaciones);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
// ConfiguraciĆ³n del DataSource por el Servidor.
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource(environment.getRequiredProperty("jndi.datasource"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.generate_statistics", "true");
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
I have a Service with this method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void processThis() throws Exception {
List<String> myList = new ArrayList<>();
myList.add("1234567890");
myDao.update(myList,'01', "1234567890");
LOGGER.info("buahhhhhhhhhhhhhhhhhhhhhhh");
}
The Dao executes an HQL query like this:
#Override
public int update(List<String> data, String data1, String data2) {
StringBuffer hql = new StringBuffer();
hql.append("UPDATE MyTablenp SET np.field1 = :d1, field2= sysdate, field3= :d2, field4= sysdate, field5= null,field6= null WHERE np.data IN (:num)");
this.getSession().getTransaction().isActive();
Query query = this.getSession().createQuery(hql.toString());
query.setParameterList("num", data);
query.setParameter("d1", data1);
query.setParameter("d2", data2);
return query.executeUpdate();
}
When i execute this, i can see the data modified in the BBDD just after dao method ends, but the service method didnt finish yet, so its commiting the HQL without finish the transaction method. Do anyone knows how to avoid that commit and commit only when service method ends?
You should use the existing session opened by hibernate at the start of the annotated method proccessThis().
In your DAO do the following:
Remove this: this.getSession().getTransaction().isActive();
and instead get current session from the session factory(assuming you have extended HibernateDaoSupport):
Session session = this.getHibernateTemplate().getSessionFactory().getCurrentSession();

Spring Boot - using JPA Repositories with Hibernate

I have a simple Spring Boot application with the following auto-configuration properties:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/mywebapp
spring.datasource.username=username
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=validate
These work fine and I'm able to setup Spring Data JpaRepositories:
public interface UserRepository extends JpaRepository<User, String>
{
User findByName(String name);
}
...for the following entities:
#Entity
public class User
{
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
protected String uuid;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String email;
}
...and use them like this:
#Transactional
public void updatePassword(String username, String password)
{
User user = userRepository.findByName(username);
user.setEmail("test#example.com"); // This gets persisted automatically by the JpaRepository.
}
Now I'm struggling to configure the same thing manually. I've tried the following:
#Configuration
#EnableTransactionManagement
public class PersistenceConfig
{
#Bean
public DataSource dataSource()
{
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/mywebapp");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan("com.example.persistent");
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "validate");
sessionFactoryBean.setHibernateProperties(hibernateProperties);
return sessionFactoryBean;
}
#Bean
public HibernateTransactionManager transactionManager()
{
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}
...but while no exceptions are thrown and i can now successfully read from the database, it seems that none of the changes I make to the entities are being persisted.
Does anyone have an idea what I'm missing for the persistence to work?
OP here.
I seem to have misunderstood that JPA needs an EntityManager instead of a session.
The following configuration works:
#Configuration
#EnableTransactionManagement
public class PersistenceJpaConfig
{
#Bean
public DataSource dataSource()
{
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/mywebapp");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.example.persistent");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.setProperty("hibernate.hbm2ddl.auto", "validate");
em.setJpaProperties(properties);
return em;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
{
return new PersistenceExceptionTranslationPostProcessor();
}
}

Spring JPA+Hibernate not able to fetch record using findAll() of JPARepository

I have created configuration using below code :
#Configuration
#EnableTransactionManagement
#ComponentScan("Name of package")
#EnableJpaRepositories("Name of package")
public class Config {
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "Name of package";
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("Driver Name");
dataSource.setUrl("Url");
dataSource.setUsername("UserName");
dataSource.setPassword("Password");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN);
entityManagerFactoryBean.setJpaProperties(hibProperties());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return entityManagerFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, "org.hibernate.dialect.DB2Dialect");
properties.put("hibernate.default_schema","Schema Name");
return properties;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
There is an custom repository interface that implements JPARepository.
I have autowired the custom repository in controller and tried to call findAll().But the method returns 0 although there are 3 records in DB.
I am using spring web for rest service calls.
Entity class is created with #Entity and #Table annotations.It has an embedded key which is annotated using #EmbeddedId annotation.
#Repository
public interface EntityRepository extends JpaRepository<EntityTable, Long> {
#SuppressWarnings("unchecked") EntityTable save(EntityTable entityTable);
}
Entity table is the name of my table mapped with db table.

Resources