Quản lý Transaction trong Hibernate?

  • Mọi người có thể giải thích giùm mình tại sao lại cần phải quản lý giao dịch trong hibernate được không ạ ?
  • Ví dụ như mỗi lần truy cập vào trang web nó sẽ query để hiển thị lên view vậy nếu người dùng reload page liên tục thì điều gì sẽ xảy ra với database ?
  • Mình có test thử nếu không close EntityManagerFactory thì có thể load lại trang còn nếu close thì xảy ra lỗi !
  • Mình đã có tìm hiểu về Level Cache để quản lý query ! Mà vẫn chưa hiểu
    :sunny: Mong mọi người giải đáp thắc mắc giùm mình với …Xin Cảm ơn <3
public class CustomerDAO {
  EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence");
  EntityManager entityManager = entityManagerFactory.createEntityManager();
  public void save(Customer customer) {
    entityManager.getTransaction().begin();
    entityManager.persist(customer);
    entityManager.getTransaction().commit();
  }
  public Customer findById(int id) {
    Customer customer = entityManager.find(Customer.class, id);
    return customer;
  }
  public List<Customer> findAll() {
    return entityManager.createQuery("SELECT c FROM Customer c", Customer.class).getResultList();
  }
  public void delete(Customer customer) {
    entityManager.getTransaction().begin();
    entityManager.remove(customer);
    entityManager.getTransaction().commit();
  }
  public void close() {
    entityManager.close();
    entityManagerFactory.close();
  }
}

Quản lý giao dịch không phải một khái niệm của Hibernate. Nó là một khái niệm của cơ sở dữ liệu. Hibernate chỉ cung cấp các API để thực hiện nó mà thôi. Còn về lý do tại sao các hệ CSDL cần phải có giao dịch (transaction) và quản lý giao dịch thì bạn có thể tham khảo từ Internet. Lý do chính là để bảo đảm tính toàn vẹn dữ liệu (integrity). Ví dụ như khi bạn dùng CSDL để chứa các thông tin khi trả hoá đơn trên mạng, trong quá trình cập nhật mỗi một lần giao dịch (transaction), bạn phải cập nhật nhiều table khác nhau nhưng khi kết thúc quá trình đó, một trong các table không được cập nhật đúng. Nếu chỉ có 1 hoặc 2 record bị ảnh hưởng, bạn có thể điều chỉnh bằng tay. Nhưng nếu hệ CSDL của bạn quá lớn, điều này là không thể.Transaction management sẽ giúp bạn giải quyết vấn đề bằng cách rollback toàn bộ các record bị ảnh hưởng, vì vậy bảo đảm rằng CSDL của bạn không có các thông tin sai lệch. Hoặc khi một record được cập nhật cùng lúc bởi nhiều user (hay process) cùng lúc, transaction management sẽ bảo đảm rằng các thông tin sẽ được cập nhật một cách có kiểm soát bằng cách khoá (lock) nó lại.

Trong trường hợp này, tuỳ theo mã của bạn. Nếu mã của bạn chỉ đơn giản là truy vấn cơ sở dữ liệu mỗi lần page được reload thì dĩ nhiên mỗi lần như vậy, sẽ có một query được sinh ra và cơ sở dữ liệu sẽ phải chạy query đó và trả về kết quả. Nhưng nếu mã của bạn “thông minh” hơn để kiểm soát các sự kiện reload, thì bạn có thể dựa vào các thông tin từ client’s IP, cookies, thời gian giữa hai lần reload kế tiếp nhau từ cùng một browser để dùng thông tin từ server cache thay vì gởi về database chẳng hạn. Tuy nhiên, nếu cuối cùng yêu cầu của chương trình của bạn vẫn là phải gới các yêu cầu này đển CSDL thì bạn phải thiết kế hệ CSDL và hạ tầng sao cho nó có thể hoạt động đủ nhanh ngay cả khi phải thực hiện nhiều query đồng thời (bạn thử hình dung và tìm hiểu làm sao các CSDL Google hay Facebook có thể hoạt động với một số lớn các truy cập đồng thời - concurrent)

Bạn không cung cấp cách gọi và sử dụng đối tượng CustomerDAO trong mã của bạn nên tôi không thể cho câu trả lời chính xác. Tuy nhiên, bạn nên chuyển các khai báo khởi tạo và kết thúc EntityManager và EntityManagerFactory trong CustomerDAO vào một lớp riêng. Theo nguyên tắc Single Responsiblity (trong SOLID), CustomerDAO chỉ nên tham chiếu (reference) đến EntityManager và EntityManagerFactory chứ không nên khởi tạo chúng. Nếu bạn làm tốt vấn đề này, nó có thể giải quyết câu hỏi của bạn. Và nếu tốt hơn nữa, bạn nên tham khảo cách sử dụng DI (Dependency Injection) khi tham chiếu đển các lớp phụ thuộc (trong trường hợp này là EntityManager và EntityManagerFactory).

Đây là một câu hỏi rất rộng. Có rất nhiều vấn đề liên quan đến Cache. Bạn không hiểu điều gì? Tương tự như khái niệm cache nói chung, cache trong Hibernate được dùng để giảm thiểu số truy cập trực tiếp vào CSDL, nhờ đó tăng hiệu năng và tốc độ (performance) của chương trình. Theo nguyên tắc chung về cache, thông tin sẽ được lưu và đọc từ cache cho đển khi có thay đổi và bắt buộc phải cập nhật CSDL. Để tăng tính hiệu quả và mềm déo, Hibernate cung cấp nhiều mức độ cache khác nhau (Cache Level).

5 Likes

Do em đọc được chỗ này ?

Lệnh entityManager.getTransaction().begin();entityManager.getTransaction().commit(); được dùng để bắt đầu và kết thúc một transaction.

Có phải mỗi lần thực hiện nhiệm vụ gì đó thì entityManager lại tạo một transaction và kết thúc nó sau khi thực hiện xong… Vậy thực hiện trong lần tiếp theo nó lại cứ tạo 1 transaction mới đúng không a.

Ở đây em có sử dụng Spring để config nó trong file bean. Theo như em hiểu thì Spring sẽ khỏi tạo toàn bộ các Bean khi chạy ứng dụng . Và default nó đều là Singleton Trong đó có PlatformTransactionManager

Overview. The PlatformTransactionManager interface is the key abstraction in the Spring transaction API, providing the classic transaction client operations: begin, commit and rollback. This interface thus provides the essential methods for controlling transactions at run time.

// Database Configuration
	@Bean
	public DataSource dataSource() {
		HikariConfig config = new HikariConfig();
		config.setDriverClassName("com.mysql.cj.jdbc.Driver");
		config.setJdbcUrl("jdbc:mysql://localhost:3306/pizza");
		config.setUsername("root");
		config.setPassword("1234");
		return new HikariDataSource(config);
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
		entityManager.setDataSource(dataSource());
		entityManager.setPackagesToScan("com.shop.entity");
		entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
		entityManager.setJpaProperties(jpaProperties());
		return entityManager;
	}

	public Properties jpaProperties() {
		Properties properties = new Properties();
		properties.setProperty("hibernate.hbm2ddl.auto", "update");
		properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
		properties.setProperty("hibernate.show_sql", "true");
		return properties;
	}

	@Bean
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
		return transactionManager;
	}
public void close() {
    entityManager.close();
    entityManagerFactory.close();
  }

Anh giải thích chỗ này cho em rõ được không ạ !

Đúng rồi bạn. Khối lệnh nằm giữa entityManager.getTransaction().begin();entityManager.getTransaction().commit(); sẽ được thực hiện trong một transaction. Ví dụ như trong method CustomerDAO.delete() mà bạn cung cấp, mỗi khi bạn gọi method này thì một Customer entity (lưu ý là tôi không gọi là Customer record mà là Customer Entity - Customer entity có thể có bao gồm nhiều record từ các table khác nhau tủy theo cách mapping của bạn) sẽ được xóa khỏi CSDL trong một transaction. Nếu có lỗi nào xảy lúc runtime làm cho quá trình này không hoàn thành, toàn bộ các record sẽ được rollback. Điều này đảm bảo rằng bạn không có tình trạng sai sót dữ liệu (ví dụ như trong trường hợp Customer Entity bao gồm các record từ 2 tables được join với nhau: Customer và Address, nếu vì lý do gì đó hệ thống chỉ delete được record từ table Customer mà không delete được từ table Address, EntityManager sẽ rollback lệnh delete này. Kết quả là cả 2 records sẽ được giữ lại trong CSDL).

3 Likes

Cho em hỏi vậy ví dụ như em muốn insert data vào 3 table riêng biệt cùng một lúc mà nó gặp lỗi ở lần save thứ 2 thì như thế nào ạ ?

  • Em nhận data trong 1 lần request tới server rồi lưu lần lượt vào CSDL 1.CustomerDAO.save(customer)
    2.ProductDAO.save(product)
    3.CompanyDAO.save(company)
    Mà nhận lỗi ở dòng 2 thì xử lý như thế nào vậy ạ ? Khi đó customer đã được lưu xuống CSDL rồi…

Cụ thể: LỖI GÌ?

4 Likes

Trên đó là em đưa ra tình huống thôi ạ ! Vd như khi save product xuống CSDL mà validate ở CSDL vd như productName varchar(40) kí tự mà client gửi về hơn 40 kí tự chẳng hạn ? Thì khi đó customer đã được lưu xuống CSDL rồi.

Cái này là do bạn tạo 3 transaction khác nhau với mỗi 1 thằng DAO. Thực tế trong 1 hệ thống, code base phức tạp hơn ví dụ của bạn, người ta không thiết kế code như thế này. Ở giữa layer BeanDAO, họ sẽ thêm 1 layer khác, thường thường ng ta gọi là Service. Layer này đảm nhiệm việc thực hiện một “business logic” mà tác động vào db. Việc này đảm bảo đc hoặc là update đc tất, hoặc là rollback mọi data.

Cả 3 bước này, họ sẽ gộp vào 1 Service. DAO chỉ có 1 nhiệm vụ duy nhất: thực hiện hành động có tác động đến db. Việc open và close transaction sẽ nằm ở trong Service. VD:

@Service
public class ExampleService {
    @PersistenceContext
    EntityManager em;
    
    @Autowired
    CustomerDAO customerDAO;
    
    @Autowired
    ProductDAO productDAO;

    @Autowired
    CompanyDAO companyDAO;
    
    public void saveAProductAndCustomerAndCompany(params...) {
        entityManager.getTransaction().begin();
        CustomerDAO.save(customer);
        ProductDAO.save(product);
        CompanyDAO.save(company);
        entityManager.getTransaction().commit();
    }
}

Ngoài việc dùng thêm 1 layer Service để ql transaction thì còn nhiều cách khác lắm. Họ có thể đóng mở transaction ngay khi user gửi request lên sv. Hệ thống sẽ detect thao tác của user đó là kiểu long-running (kiểu mua hàng có nhiều bước ý) thì open transaction, và đợi đến khi nào user kết thúc 1 chuỗi thao tác thì close để tiết kiệm performance. Hoặc cũng có thể là là one-time (chỉ load hoặc cập nhật data đúng trong lần request đó) thì mở xong cái đóng luôn…

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?