Chia sẻ kiến thức IT và đời sống – Sharing IT Knowledge & Life Insights

Hiển thị các bài đăng có nhãn Java Data Access. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Java Data Access. Hiển thị tất cả bài đăng

Thứ Hai, 1 tháng 1, 2024

JPA cơ bản

Java Persistence API cơ bản

1. JPA là gì?

Java Persistence API (JPA) là đặc tả (specification) chuẩn của Java để ánh xạ đối tượng Java với các bảng trong cơ sở dữ liệu quan hệ (ORM) và để lưu trữ dữ liệu một cách bền vững (persistence). JPA ban đầu thuộc Java EE (Oracle) và hiện được phát triển bởi Eclipse Foundation với tên Jakarta Persistence.

JPA có Java Persistence Query Language (JPQL), một ngôn ngữ truy vấn hướng đối tượng, độc lập với từng loại database. Nhờ vậy, ứng dụng có thể đổi database (MySQL, PostgreSQL, Oracle...) mà không cần sửa code.

Lưu ý: JPA chỉ là đặc tả. Để chạy được cần một implementation (JPA provider).

Các JPA provider phổ biến

  • Hibernate (phổ biến nhất)
  • EclipseLink (reference implementation)
  • OpenJPA
  • DataNucleus (hỗ trợ cả JPA và JDO)

Ý tưởng chính: nếu chỉ dùng API chuẩn của JPA, bạn có thể thay đổi provider mà không sửa code. Tuy nhiên Hibernate còn cung cấp native API (HQL, Criteria của Hibernate, Session...) với nhiều tính năng vượt ngoài JPA, đổi lại bạn bị phụ thuộc vào Hibernate.

2. JPA và JDO

JDO (Java Data Objects) và JPA đều là đặc tả, không phải implementation, và đều là chuẩn persistence của Java.

JPAJDO
Datastore hỗ trợChỉ RDBMSMọi loại datastore (RDBMS, NoSQL, object DB...)
Mức độ phổ biếnRất caoThấp hơn
ImplementationHibernate, EclipseLink, OpenJPA...DataNucleus...

Hibernate không implement JDO. Nếu cần JDO, hãy tham khảo DataNucleus.

3. Lịch sử phát triển

Quá trình tiến hoá của các công nghệ persistence trong Java:

  1. JDBC: thao tác SQL trực tiếp
  2. Data Mapper (MyBatis...)
  3. EJB (Entity Bean)
  4. JDO
  5. JPA
    • JPA 1.0 (cùng EJB 3.0, 2006)
    • JPA 2.0 (2009)
    • JPA 2.1 (2013)
    • JPA 2.2 (2017)
    • Jakarta Persistence 3.0 (2020, đổi package sang jakarta.persistence)
    • Jakarta Persistence 3.1 (2022) và 3.2 (2024)

4. Cấu trúc của JPA

JPA gồm 4 phần:

  • The Java Persistence API: EntityManager, EntityManagerFactory, annotation...
  • The Criteria API: xây dựng truy vấn bằng code, an toàn kiểu (type-safe)
  • Query language (JPQL)
  • Object-relational mapping metadata: annotation hoặc XML (@Entity, @Table, @Column...)

5. Ví dụ nhanh

Entity

java
@Entity
@Table(name = "customers")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    // constructor mặc định (bắt buộc), getter/setter
}

persistence.xml (đặt trong META-INF/)

xml
<persistence-unit name="demoPU" transaction-type="RESOURCE_LOCAL">
    <class>com.example.Customer</class>
    <properties>
        <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/demo"/>
        <property name="jakarta.persistence.jdbc.user" value="root"/>
        <property name="jakarta.persistence.jdbc.password" value="secret"/>
        <!-- Hibernate dialect, thường tự nhận diện ở bản mới -->
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
    </properties>
</persistence-unit>

Danh sách dialect: Hibernate Configuration (TutorialsPoint)

Dùng EntityManager (CRUD)

java
EntityManagerFactory emf = Persistence.createEntityManagerFactory("demoPU");
EntityManager em = emf.createEntityManager();

em.getTransaction().begin();
Customer c = new Customer();
c.setName("An");
em.persist(c);                         // Create
em.getTransaction().commit();

Customer found = em.find(Customer.class, c.getId());   // Read

em.getTransaction().begin();
found.setName("Binh");                 // Update (tự động flush khi commit)
em.getTransaction().commit();

em.getTransaction().begin();
em.remove(found);                      // Delete
em.getTransaction().commit();

em.close();
emf.close();

find() vs getReference()

  • find() truy vấn DB ngay và trả về entity (hoặc null nếu không có).
  • getReference() trả về một proxy lazy, chỉ truy vấn khi truy cập dữ liệu. Nếu không tồn tại sẽ ném EntityNotFoundException. Hữu ích khi chỉ cần tham chiếu khoá ngoại mà không cần tải toàn bộ entity.

6. Vòng đời của Entity

Trạng tháiMô tả
New / TransientVừa tạo bằng new, chưa gắn với persistence context
ManagedĐược quản lý bởi EntityManager, thay đổi sẽ tự được đồng bộ xuống DB
DetachedĐã tách khỏi persistence context (sau close(), detach())
RemovedĐã đánh dấu xoá, sẽ bị xoá khi commit

7. Ánh xạ quan hệ (Relationship Mapping)

4 loại quan hệ

  • @OneToOne
  • @ManyToOne
  • @OneToMany
  • @ManyToMany

Mỗi quan hệ có thể là một chiều (unidirectional) hoặc hai chiều (bidirectional), và có thể tải ngay (EAGER) hoặc theo yêu cầu (LAZY).

Mặc định: @OneToOne và @ManyToOne là EAGER, @OneToMany và @ManyToMany là LAZY.

Owning side và Inverse side

Mỗi quan hệ có hai phía:

  • Owning side: chịu trách nhiệm cập nhật quan hệ xuống database, thường là phía giữ khoá ngoại.
  • Inverse side: phía còn lại, ánh xạ ngược về owning side bằng thuộc tính mappedBy.

Nếu bạn quen Hibernate, mappedBy của JPA tương đương thuộc tính inverse của Hibernate: phía inverse sẽ bị bỏ qua khi cập nhật quan hệ.

java
@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;

    // Inverse side
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
}

@Entity
public class OrderItem {
    @Id @GeneratedValue
    private Long id;

    // Owning side (giữ khoá ngoại order_id)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;
}

Cascade

CascadeType gồm PERSIST, MERGE, REMOVE, REFRESH, DETACH, ALL. Dùng để lan truyền thao tác từ entity cha sang entity con.

Embedded object và ElementCollection

  • @Embeddable / @Embedded / @EmbeddedId: nhúng một đối tượng giá trị (ví dụ Address) hoặc khoá chính tổ hợp. Xem JPA EmbeddedId example
  • @ElementCollection: lưu tập hợp các giá trị cơ bản hoặc embeddable. Xem ElementCollection (Wikibooks)

8. Truy vấn trong JPA

Có 3 cách truy vấn:

  1. Native query: SQL thuần
  2. JPQL (Java Persistence Query Language, SQL hướng đối tượng): Query, TypedQuery, @NamedQuery / @NamedQueries
  3. Criteria API: xây dựng truy vấn bằng code, động và type-safe
java
// JPQL với tham số (khuyến nghị, tránh SQL injection)
TypedQuery<Customer> q = em.createQuery(
    "SELECT c FROM Customer c WHERE c.name = :name", Customer.class);
q.setParameter("name", "An");
List<Customer> list = q.getResultList();

// Named Query
@Entity
@NamedQuery(name = "Customer.findByName",
            query = "SELECT c FROM Customer c WHERE c.name = :name")
public class Customer { ... }

em.createNamedQuery("Customer.findByName", Customer.class)
  .setParameter("name", "An")
  .getResultList();

// Criteria API
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> root = cq.from(Customer.class);
cq.select(root).where(cb.equal(root.get("name"), "An"));
List<Customer> result = em.createQuery(cq).getResultList();
  • Named Query là truy vấn tĩnh, được parse và kiểm tra cú pháp khi khởi động ứng dụng, dễ tái sử dụng.
  • Truy vấn động (dynamic) (JPQL hoặc native) được tạo lúc runtime.
  • Criteria API linh hoạt hơn và an toàn kiểu hơn khi cần xây dựng điều kiện động.
  • Luôn dùng parameter binding (:name, ?1) thay vì nối chuỗi để chống SQL injection.

9. Kế thừa (Inheritance)

JPA có 3 chiến lược ánh xạ kế thừa:

Chiến lượcMô tảƯu điểmNhược điểm
SINGLE_TABLE (mặc định)Cả hệ thống phân cấp lớp vào một bảng (kèm cột phân biệt DTYPE)Không cần JOIN, truy vấn nhanhNhiều cột NULL, không đặt được NOT NULL cho cột lớp con
JOINEDMỗi lớp một bảng, bảng con chỉ chứa cột riêng và join với bảng cha qua khoá chínhDữ liệu chuẩn hoá, không trùng lặpPhải JOIN nên truy vấn chậm hơn
TABLE_PER_CLASSMỗi lớp cụ thể một bảng chứa đầy đủ cột (kể cả cột kế thừa)Truy vấn lớp cụ thể không cần JOINDữ liệu chung bị lặp ở nhiều bảng, truy vấn đa hình dùng UNION; là tính năng tuỳ chọn trong spec

Ví dụ với JOINED, đặt annotation ở entity cha:

java
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Vehicle { ... }

@Entity
public class Car extends Vehicle { ... }

Ngoài ra có @MappedSuperclass: lớp cha chỉ để dùng chung các trường (ví dụ id, createdAt), không phải entity và không có bảng riêng.

Ghi chú (legacy) về Google App Engine: với Datastore cũ dùng DataNucleus, JOINED và SINGLE_TABLE không được hỗ trợ; TABLE_PER_CLASS và MAPPED_SUPERCLASS hoạt động như mô tả trong tài liệu DataNucleus.

10. Một số lưu ý thực tế (Best practices)

  • N+1 problem: duyệt quan hệ LAZY trong vòng lặp có thể sinh rất nhiều câu SQL. Khắc phục bằng JOIN FETCH, @EntityGraph hoặc batch fetching.
  • Ưu tiên FetchType.LAZY cho mọi quan hệ, chỉ dùng EAGER khi thật sự cần.
  • Luôn có constructor mặc định (không tham số) cho entity, và override equals() / hashCode() cẩn thận.
  • Với quan hệ hai chiều, hãy đồng bộ cả hai phía bằng phương thức tiện ích (ví dụ order.addItem(item)).
  • Đừng dùng hbm2ddl.auto=update trên production; hãy dùng công cụ migration như Flyway hoặc Liquibase.
  • Trong Spring, dùng Spring Data JPA (JpaRepository) để giảm code lặp, nhưng vẫn cần hiểu JPA bên dưới.

Reference:

Thứ Hai, 26 tháng 7, 2021

Creating a Batch Service

To start from scratch, move on to Starting with Spring Initializr.

To skip the basics, do the following:

When you finish, you can check your results against the code in gs-batch-processing/complete.

Business Data

Typically, your customer or a business analyst supplies a spreadsheet. For this simple example, you can find some made-up data in src/main/resources/sample-data.csv:

Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe

This spreadsheet contains a first name and a last name on each row, separated by a comma. This is a fairly common pattern that Spring can handle without customization.

Spring batch introduction

Spring Batch is a lightweight, comprehensive batch framework designed to enable the development of robust batch applications vital for the daily operations of enterprise systems. Spring Batch builds upon the characteristics of the Spring Framework that people have come to expect (productivity, POJO-based development approach, and general ease of use), while making it easy for developers to access and leverage more advance enterprise services when necessary. Spring Batch is not a scheduling framework. There are many good enterprise schedulers (such as Quartz, Tivoli, Control-M, etc.) available in both the commercial and open source spaces. It is intended to work in conjunction with a scheduler, not replace a scheduler. 

Spring Batch is also a minimalistic framework to run batch processing applications. It provides reusable functions that are essential in processing large volumes of records, including logging/tracing, transaction management, job processing statistics, job restart, skip, and resource management. It also provides more advanced technical services and features that will enable extremely high-volume and high-performance batch jobs through optimization and partitioning techniques. Simple as well as complex, high-volume batch jobs can leverage the framework in a highly scalable manner to process significant volumes of information.

Spring Batch provides reusable functions that are essential in processing large volumes of records, including logging/tracing, transaction management, job processing statistics, job restart, skip, and resource management. It also provides more advanced technical services and features that enable extremely high-volume and high performance batch jobs through optimization and partitioning techniques. Spring Batch can be used in both simple use cases (such as reading a file into a database or running a stored procedure) as well as complex, high volume use cases (such as moving high volumes of data between databases, transforming it, and so on). High-volume batch jobs can leverage the framework in a highly scalable manner to process significant volumes of information.

Thứ Ba, 2 tháng 3, 2021

Beginning Amazon DynamoDB

What Is Amazon DynamoDB?

Amazon DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability. 

NoSQL is a term used to describe nonrelational database systems that are highly available, scalable, and optimized for high performance. Instead of the relational model, NoSQL databases (like DynamoDB) use alternate models for data management, such as key-value pairs or document storage. For more information, see http://aws.amazon.com/nosql.

Thứ Ba, 27 tháng 10, 2020

MyBatis introduction

 MyBatis is a Java persistence framework that couples objects with stored procedures or SQL statements using an XML descriptor or annotations.

MyBatis is free software that is distributed under the Apache License 2.0.

MyBatis is a fork of iBATIS 3.0 and is maintained by a team that includes the original creators of iBATIS. 

MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records

Chủ Nhật, 23 tháng 7, 2017

JPA Criteria API

Using the Criteria API to Create Queries

The Criteria API is used to define queries for entities and their persistent state by creating query-defining objects. Criteria queries are written using Java programming language APIs, are typesafe, and are portable. Such queries work regardless of the underlying data store.
The following topics are addressed here:

0.1 Overview of the Criteria and Metamodel APIs

Similar to JPQL, the Criteria API is based on the abstract schema of persistent entities, their relationships, and embedded objects. The Criteria API operates on this abstract schema to allow developers to find, modify, and delete persistent entities by invoking Java Persistence API entity operations. The Metamodel API works in concert with the Criteria API to model persistent entity classes for Criteria queries.
The Criteria API and JPQL are closely related and are designed to allow similar operations in their queries. Developers familiar with JPQL syntax will find equivalent object-level operations in the Criteria API.
The following simple Criteria query returns all instances of the Pet entity in the data source:
EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> allPets = q.getResultList();
The equivalent JPQL query is
SELECT p
FROM Pet p

JPA Select Statements and NamedQuery

Select Statements

A select query has six clauses: SELECT, FROM, WHERE, GROUP BY, HAVING, and ORDER BY. The SELECT and FROM clauses are required, but the WHERE, GROUP BY, HAVING, and ORDER BY clauses are optional. Here is the high-level BNF syntax of a query language select query:
     Note: some of the terms referred to in this chapter.
  • Abstract schema: The persistent schema abstraction (persistent entities, their state, and their relationships) over which queries operate. The query language translates queries over this persistent schema abstraction into queries that are executed over the database schema to which entities are mapped.
  • Abstract schema type: The type to which the persistent property of an entity evaluates in the abstract schema. That is, each persistent field or property in an entity has a corresponding state field of the same type in the abstract schema. The abstract schema type of an entity is derived from the entity class and the metadata information provided by Java language annotations.
  • Backus-Naur Form (BNF): A notation that describes the syntax of high-level languages. The syntax diagrams in this chapter are in BNF notation.
  • Navigation: The traversal of relationships in a query language expression. The navigation operator is a period.
  • Path expression: An expression that navigates to an entity's state or relationship field.
  • State field: A persistent field of an entity.
  • Relationship field: A persistent field of an entity whose type is the abstract schema type of the related entity.
QL_statement ::= select_clause from_clause 
  [where_clause][groupby_clause][having_clause][orderby_clause]

Ordinary JPA Query API

Queries are represented in JPA 2 by two interfaces - the old Query interface, which was the only interface available for representing queries in JPA 1, and the new TypedQuery interface that was introduced in JPA 2. The TypedQuery interface extends the Query interface.
In JPA 2 the Query interface should be used mainly when the query result type is unknown or when a query returns polymorphic results and the lowest known common denominator of all the result objects is Object. When a more specific result type is expected queries should usually use the TypedQuery interface. It is easier to run queries and process the query results in a type safe manner when using the TypedQuery interface.
Ngoài query thông thường dùng Query hoặc TypedQuery thì còn có JPA Criteria API  và named queries 

Building Queries with createQuery

As with most other operations in JPA, using queries starts with an EntityManager (represented by em in the following code snippets), which serves as a factory for both Query and TypedQuery:
  Query q1 = em.createQuery("SELECT c FROM Country c");
 
  TypedQuery<Country> q2 =
      em.createQuery("SELECT c FROM Country c", Country.class);

Thứ Ba, 29 tháng 11, 2016

JavaDB Connection pool

From Wikipedia, the free encyclopedia

In software engineering, a connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required.[citation needed] Connection pools are used to enhance the performance of executing commands on a database. Opening and maintaining a database connection for each user, especially requests made to a dynamic database-driven website application, is costly and wastes resources. In connection pooling, after a connection is created, it is placed in the pool and it is used again so that a new connection does not have to be established. If all the connections are being used, a new connection is made and is added to the pool. Connection pooling also cuts down on the amount of time a user must wait to establish a connection to the database.

Applications

Web-based and enterprise applications use an application server to handle connection pooling. Dynamic web pages without connection pooling open connections to database services as required and close them when the page is done servicing a particular request. Pages that use connection pooling, on the other hand, maintain open connections in a pool. When the page requires access to the database, it simply uses an existing connection from the pool, and establishes a new connection only if no pooled connections are available. This reduces the overhead associated with connecting to the database to service individual requests.
Local applications that need frequent access to databases can also benefit from connection pooling. Open connections can be maintained in local applications that don't need to service separate remote requests like application servers, but implementations of connection pooling can become complicated. A number of available libraries implement connection pooling and related SQL query pooling, simplifying the implementation of connection pools in database-intensive applications.
Administrators can configure connection pools with restrictions on the numbers of minimum connections, maximum connections and idle connections to optimize the performance of pooling in specific problem contexts and in specific environments.

Database support

Connection pooling is supported by IBM DB2,[1] Microsoft SQL Server,[2] Oracle,[3] MySQL,[4] and PostgreSQL.[5]

How to configure the C3P0 connection pool in Hibernate

Connection Pool
Connection pool is good for performance, as it prevents Java application create a connection each time when interact with database and minimizes the cost of opening and closing connections.
Hibernate comes with internal connection pool, but not suitable for production use. In this tutorial, we show you how to integrate third party connection pool – C3P0, with Hibernate.

1. Get hibernate-c3p0.jar

To integrate c3p0 with Hibernate, you need hibernate-c3p0.jar, get it from JBoss repository.
File : pom.xml
<project ...>

 <repositories>
  <repository>
   <id>JBoss repository</id>
   <url>http://repository.jboss.org/nexus/content/groups/public/</url>
  </repository>
 </repositories>

 <dependencies>

  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>3.6.3.Final</version>
  </dependency>

  <!-- Hibernate c3p0 connection pool -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-c3p0</artifactId>
   <version>3.6.3.Final</version>
  </dependency>

 </dependencies>
</project>

2. Configure c3p0 properties

To configure c3p0, puts the c3p0 configuration details in “hibernate.cfg.xml“, like this :
File : hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory>
  <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:MKYONG</property>
  <property name="hibernate.connection.username">mkyong</property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="hibernate.default_schema">MKYONG</property>
  <property name="show_sql">true</property>

  <property name="hibernate.c3p0.min_size">5</property>
  <property name="hibernate.c3p0.max_size">20</property>
  <property name="hibernate.c3p0.timeout">300</property>
  <property name="hibernate.c3p0.max_statements">50</property>
  <property name="hibernate.c3p0.idle_test_period">3000</property>

  <mapping class="com.mkyong.user.DBUser"></mapping>
</session-factory>
</hibernate-configuration>
  1. hibernate.c3p0.min_size – Minimum number of JDBC connections in the pool. Hibernate default: 1
  2. hibernate.c3p0.max_size – Maximum number of JDBC connections in the pool. Hibernate default: 100
  3. hibernate.c3p0.timeout – When an idle connection is removed from the pool (in second). Hibernate default: 0, never expire.
  4. hibernate.c3p0.max_statements – Number of prepared statements will be cached. Increase performance. Hibernate default: 0 , caching is disable.
  5. hibernate.c3p0.idle_test_period – idle time in seconds before a connection is automatically validated. Hibernate default: 0
Note
For detail about hibernate-c3p0 configuration settings, please read this article.

Understanding Java Database Connection Pooling Properties

Basic properties controlling pooling behaviour.
PropertyExplanation
minpoolMinimum number of connections that should be held in the pool.
maxpoolMaximum number of connections that may be held in the pool.
maxsizeMaximum number of connections that can be created for use.
idleTimeoutThe idle timeout for connections (seconds).
  
Bean properties supported by snaq.db.DBPoolDataSource (can also be specified via snaq.db.DBPoolDataSourceFactory).
PropertyDescription
nameName of the DataSource, which is also used to assign a ConnectionPool name.
descriptionDescription for the DataSource.
driverClassNameFully-qualified class name of JDBC Driver to use.
urlJDBC URL to connect to the database.
userUsername for database connections.
passwordPassword for database connections.
passwordDecoderClassNameFully-qualified class name of snaq.db.PasswordDecoder implementation to use.
(It must have a public no-argument constructor).
minPoolMinimum number of pooled connections to maintain.
maxPoolMaximum number of pooled connections to maintain.
maxSizeMaximum number of connection that can be created.
idleTimeoutIdle timeout of pooled connections (seconds).
loginTimeoutTimeout for database connection attempts (seconds).
validatorClassNameFully-qualified class name of snaq.db.ConnectionValidator implementation to use.
(It must have a public no-argument constructor).
validatorQuery*Query string to use for validation, if validatorClassName not specified.
This is passed to a snaq.db.SimpleQueryValidator instance.

Hibernate-Supported Connection Pools

Table 10.1. Hibernate-Supported Connection Pools

c3p0
Distributed with Hibernate
Apache DBCP
Apache Pool
Proxool
JDBC Pooling Wrapper
Based on:

PHỔ BIẾN

NHÃN

LƯỢT XEM