Hibernate là một ORM được sử dụng phổ biến trong các ứng dụng SpringBoot, nhưng với cách tiếp cận hiện nay trong hầu hết các ứng dụng SpringBoot, mặc định SpingBoot sử dụng Spring data JPA để auto config. Mục đích của Spring Data JPA là giảm thiểu việc thực hiện quá nhiều bước để có thể implement JPA. Nhưng để làm việc trực tiếp với Hibernate Native API trong bài này chúng ta sẽ đi tìm hiểu cách config SessionFactory trong SpringBoot và cách sử dụng các Native API của Hibernate.
SessionFactory
là một interface giúp tạo ra session kết nối đến database bằng cách đọc các cấu hình trong Hibernate configuration.
Có 2 cách để sử dụng Session của Hibernate native API trong SpringBoot:
- Unwrap từ JPA:
Session session = entityManager.unwrap( Session.class );
SessionImplementor sessionImplementor = entityManager.unwrap( SessionImplementor.class );
SessionFactory sessionFactory = entityManager.getEntityManagerFactory().unwrap( SessionFactory.class );
- Configuration bean:
hibernate-config.properties
cafeit.entity.package=info.cafeit.hibernate-native-api-spring.entity
cafeit.datasource.driver=org.h2.Driver
cafeit.datasource.url=jdbc:h2:mem:testdb
cafeit.datasource.username=root
cafeit.datasource.password=
cafeit.datasource.dialect=org.hibernate.dialect.H2Dialect
cafeit.datasource.hbm2ddl.auto=validate
cafeit.datasource.show-sql=TRUE
HibernateSessionFactoryConfig.java
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:hibernate-config.properties")
public class HibernateSessionFactoryConfig {
@Value("${cafeit.entity.package}")
private String entityPackageScan;
@Value("${cafeit.datasource.driver}")
private String driverClassName;
@Value("${cafeit.datasource.url}")
private String dataSourceUrl;
@Value("${cafeit.datasource.username}")
private String userName;
@Value("${cafeit.datasource.password}")
private String password;
@Value("${cafeit.datasource.dialect}")
private String dialect;
@Value("${cafeit.datasource.hbm2ddl.auto}")
private String hbm2ddlAuto;
@Value("${cafeit.datasource.show-sql}")
private String showSql;
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(entityPackageScan);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName(driverClassName);
config.setJdbcUrl(dataSourceUrl);
config.setUsername(userName);
config.setPassword(password);
config.setAutoCommit(false);
return new HikariDataSource(config);
}
@Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(SHOW_SQL, showSql);
hibernateProperties.setProperty(
AvailableSettings.HBM2DDL_AUTO, hbm2ddlAuto);
hibernateProperties.setProperty(
AvailableSettings.DIALECT, dialect);
return hibernateProperties;
}
}
Trong đó:
StandardServiceRegistryBuilder
như các ứng dụng Java EE để tạo ra SessionFactory
thì trong Spring chúng ta sẽ sử dụng LocalSessionFactoryBean
để cấu hình và share SessionFactory
cho Spring application context. Sau khi share thì chúng ta có thể inject bean SessionFactory
như một Spring bean thông thường.SessionFactory
, sau khi đăng ký thì chúng ta có thể sữ dụng @Transactional
or TransactionTemplate
để quản lý transaction.SessionFactory
là một đối tượng luồng an toàn (Thread-safe) và được sử dụng bởi tất cả các luồng trong ứng dụng dẫn đến việc khởi tạo đối tượng SessionFactory
mất khá nhiều tài nguyên nên nó thường được tạo ra trong quá trình khởi động ứng dụng và lưu giữ để sử dụng sau này.
Mỗi database cần phải có một session factory, nên nếu chúng ta sử dụng nhiều cơ sở dữ liệu thì bạn cũng phải tạo đối tượng SessionFactory
cho từng database tương ứng.
Session được sử dụng để tạo ra kết nối vật lý với một cơ sở dữ liệu. Đối tượng Session được thiết kế để được tạo ra thể hiện mỗi khi tương tác với cơ sở dữ liệu, các persistent object được lưu và truy xuất thông qua một đối tượng Session. Các đối tượng Session không nên mở trong thời gian dài bởi vì chúng là unsafe thread nên cần được tạo ra và đóng khi cần thiết.
Chúng ta có thể sử dụng method openSession()
hoặc getCurrentSession()
để mở một session. Nếu dùng openSession()
nó sẽ mở một session mới, còn nếu dùng getCurrentSession()
nó sẽ lấy session từ thread context đang tồn tại.
- Method getCurrentSession()
Để sử dụng thì đầu tiên cần phải cấu hình hibernate.current_session_context_class
hibernateProperties.setProperty(
AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS, "org.hibernate.context.internal.ThreadLocalSessionContext");
Nếu không có cấu hình trên thì bạn sẽ gặp lỗi:
org.hibernate.HibernateException: No CurrentSessionContext configured!
Khi sử dụng getCurrentSession()
thì session sẽ tự động đẩy dữ liệu (flush()
) và đóng (close()
) session sau mỗi lần commit.
Cùng xem ví dụ bên dưới:
@SpringBootApplication
public class HibernateNativeAPIApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(HibernateNativeAPIApplication.class, args);
}
@Autowired
SessionFactory sessionFactory;
@Override
@Transactional
public void run(String... args) throws Exception {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Users u1 = session.load(Users.class, 1l);
session.getTransaction().commit();
Users u2 = session.load(Users.class, 2l);
System.out.println(u1);
System.out.println(u2);
}
}
Sau khi kết thúc một phiên thì getCurrentSession()
sẽ tự động đóng session nên trường hợp trên nó sẽ bắn ra exception vì session đã bị close.
Caused by: java.lang.IllegalStateException: Session/EntityManager is closed
- Method openSession()
Đối với phương thức openSession()
, sau khi truy vấn dữ liệu (thêm, xóa, sửa) thì session vẫn còn giữ và không tự động đẩy (flush()
) hay close mà bạn phải tự làm việc này.
@SpringBootApplication
public class HibernateNativeAPIApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(HibernateNativeAPIApplication.class, args);
}
@Autowired
SessionFactory sessionFactory;
@Override
@Transactional
public void run(String... args) throws Exception {
Session session = sessionFactory.openSession();
session.beginTransaction();
Users u1 = session.load(Users.class, 1l);
session.getTransaction().commit();
Users u2 = session.load(Users.class, 2l);
System.out.println(u1);
System.out.println(u2);
}
}
Vì openSession()
sau khi kết thúc một commit thì session vẫn được giữ đến khi chúng ta gọi close nên để tránh memory leak chúng ta cần quản lý close session đúng cách.
Trong bài này chúng ta đã tìm hiểu cách config SessionFactory
trong Springboot, bài sau chúng ta sẽ đi tìm hiểu Lifecycle của Entity và các API của Session. Mọi thắc mắc hoắc đóng góp giúp bài viết hoạn thiện hơn vui lòng comment ở bên dưới. Happy learning :D
Nguồn: cafeit.info