Kết nối cơ sở dữ liệu - chúng có nên được truyền dưới dạng tham số không?


11

Chúng tôi có một hệ thống theo đó kết nối cơ sở dữ liệu được nhận một lần bằng cách sử dụng một phương thức chung và được truyền qua lớp có liên quan sẽ được sử dụng. Có nghi ngờ rằng việc truyền kết nối cơ sở dữ liệu như một tham số cho các lớp khác nhau sẽ gây ra vấn đề, vì vậy tôi đang kiểm tra xem liệu điều này có thực sự khả thi hay không, và có cách nào tốt hơn để làm điều đó không?

Tôi biết có một số công cụ ORM để thực hiện sự kiên trì nhưng chúng ta không thể đi sâu vào vấn đề đó ..

Bất kỳ thông tin phản hồi đều được hoan nghênh, cảm ơn.


Những loại vấn đề bạn đang đề cập đến? Ai có những nghi ngờ này? (Không phải bạn, tôi giả sử.)
Greg Hewgill

1
Các vấn đề như khiến nhà phát triển quên đóng kết nối, nói chung tôi chỉ đang cố gắng xem liệu đó có phải là một cách thực hành tốt để chuyển kết nối cơ sở dữ liệu sang các phương thức khác nhau làm tham số hay không. Ya những nghi ngờ đến từ một nhà phát triển khác.
ipohfly

Câu trả lời:


8

Có nó là an toàn để vượt qua một kết nối. Bạn xử lý kết nối trong một khối điều khiển bên ngoài. Không có gì không an toàn về nó.

Điều không an toàn là viết mã không đảm bảo kết nối được xử lý đúng cách một cách kịp thời. Quên làm sạch một tài nguyên không liên quan đến việc chuyển nó đi khắp nơi. Bạn có thể dễ dàng viết mã để lại kết nối treo mà không cần chuyển nó đi bất cứ đâu.

Trong C ++, bạn được RAII bảo vệ nếu bạn phân bổ trên ngăn xếp hoặc sử dụng các con trỏ thông minh. Trong C # đưa ra một quy tắc cứng rằng tất cả các đối tượng dùng một lần (như kết nối) được khai báo trong khối "sử dụng". Trong Java dọn dẹp với logic thử cuối cùng. Có đánh giá mã trên tất cả các mã lớp dữ liệu để đảm bảo điều này.

Trường hợp sử dụng phổ biến nhất là khi bạn có một số thao tác có thể được kết hợp theo nhiều hoán vị. Và mỗi hoán vị này cần phải là một giao dịch nguyên tử (tất cả thành công hoặc quay trở lại). sau đó bạn phải chuyển giao dịch (và do đó kết nối tương ứng) xung quanh tất cả các phương thức.

Giả sử chúng ta có nhiều hành động foobar () có thể được kết hợp theo nhiều cách khác nhau như các giao dịch nguyên tử.

//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
    conn.Open();
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {//inner foobar actions just do their thing. They don't need to clean up.
            foobar1(tran);
            foobar2(tran);
            foobar3(tran);
            tran.Commit();
        }
        catch (Exception ex)
        { tran.Rollback(); }
    }
}//connection is returned to the pool automatically

BTW bạn sẽ muốn mở các kết nối càng sớm càng tốt, loại bỏ chúng càng sớm càng tốt. Đồng đội của bạn có thể đúng nếu bạn coi các kết nối là thành viên đối tượng, giới thiệu họ là trạng thái không cần thiết và để kết nối mở lâu hơn mức cần thiết. Nhưng hành động chuyển một kết nối hoặc giao dịch như một tham số không phải là sai.

BTW. Tùy thuộc vào sự hỗ trợ ngôn ngữ của bạn cho các chức năng hạng nhất, bạn có thể thực hiện một danh sách các hành động foobar (). Vì vậy, một chức năng có thể xử lý tất cả các hoán vị của các hành động. Loại bỏ sự trùng lặp của khối điều khiển bên ngoài cho mỗi hoán vị.


đánh dấu đây là câu trả lời vì nó cho tôi thêm ý tưởng về tình hình hiện tại
ipohfly

6

Nghe có vẻ như bạn sau khi tiêm Dependency . Đó là, kết nối gộp được tạo một lần và được tiêm bất cứ nơi nào cần thiết. Chắc chắn truyền kết nối thông qua một tham số phương thức là một cách để tiêm phụ thuộc, nhưng một bộ chứa IoC như Guice, PicoContainer hoặc Spring là một cách khác (an toàn hơn) bạn có thể làm điều này.

Sử dụng DI có nghĩa là bạn có thể kết thúc gọn gàng logic xung quanh việc tạo, mở, sử dụng và đóng kết nối - tránh xa logic kinh doanh cốt lõi của bạn.

Spring JDBC et al là những ví dụ điển hình về việc thực hiện loại hành vi này cho bạn


Emm không thực sự nhìn vào tiêm phụ thuộc. Chỉ cần cố gắng tìm hiểu xem nói chung có phải là một cách thực hành tốt hay không, và nếu không, cách quản lý kết nối cơ sở dữ liệu tốt hơn là gì (DI là một cách để làm điều đó).
ipohfly

-1. Một kết nối không phù hợp với hệ thống nhiều người dùng. Nó có thể hoạt động do khối lượng người dùng thấp và thực hiện nhanh. Với việc gộp chung, tốt hơn là khởi tạo một đối tượng kết nối cho mỗi hành động ngay cả trong một hệ thống người dùng.
mike30

2

Vượt qua những thứ cơ sở dữ liệu hơn là những thứ dữ liệu có thể dẫn đến các vấn đề. Ở mức độ đó, bất cứ khi nào nó thực tế, không vượt qua một điều cơ sở dữ liệu trừ khi người ta có thể đảm bảo vệ sinh cơ sở dữ liệu thích hợp.

Vấn đề với việc chuyển xung quanh cơ sở dữ liệu là nó có thể cẩu thả. Tôi đã thấy nhiều lỗi trong mã với ai đó đi qua kết nối cơ sở dữ liệu, rằng ai đó sau đó lấy một kết quả được đặt và đâm vào một đối tượng cục bộ (tập kết quả, vẫn được kết nối với cơ sở dữ liệu) và sau đó liên kết một con trỏ trong cơ sở dữ liệu trong một thời gian đáng kể. Một trường hợp khác, một người nào đó đã chuyển một tập kết quả cho người khác (sau đó được đặt) và sau đó phương thức đã chuyển tập kết quả đã đóng nó (và câu lệnh) dẫn đến lỗi khi các phương thức khác cố gắng làm việc với tập kết quả không còn nữa.

Tất cả những điều này bắt nguồn từ việc không tôn trọng cơ sở dữ liệu, kết nối, tuyên bố, tập kết quả và vòng đời của chúng.

Để tránh điều này, có các mẫu và cấu trúc hiện có hoạt động tốt hơn với cơ sở dữ liệu và không có cơ sở dữ liệu, mọi thứ cần phải thoát ra khỏi các lớp mà chúng bị giới hạn. Dữ liệu đi vào, dữ liệu bị mất, cơ sở dữ liệu vẫn được đặt.


1
Các kết nối +1 db nên có thời gian ngắn nhất có thể. Mở nó, sử dụng nó, đóng nó càng nhanh càng tốt. Ngày nay có rất nhiều triển khai nhóm kết nối vì vậy sử dụng kết nối cho nhiều hoạt động là một nền kinh tế sai lầm. Và lời mời cho các lỗi hoặc sự cố về hiệu suất (giữ khóa trên các bảng, sử dụng tài nguyên kết nối)
jqa

Tên của một số các mô hình và cấu trúc hiện có là gì?
Daniel Kaplan

@tieTYT Những cái chính đi đầu là đối tượng truy cập Dữ liệu dùng để ẩn cơ sở dữ liệu khỏi phần còn lại của ứng dụng. Xem thêm Lớp truy cập dữ liệuÁnh xạ quan hệ đối tượng

Khi tôi nghĩ về những mô hình đó, tôi cảm thấy chúng ở mức độ trừu tượng cao hơn những gì anh ấy hỏi về. Giả sử bạn có cách lấy Roottừ Dao. Nhưng sau đó bạn nhận ra bạn cũng muốn có một cách để có được Nodemà không cần rút toàn bộ Rootđối tượng với nó. Làm thế nào để bạn thực hiện RootDao gọi Nodemã Dao (nghĩa là: tái sử dụng), nhưng đảm bảo NodeDao chỉ đóng kết nối khi NodeDao được gọi trực tiếp và giữ kết nối mở khi RootDao được gọi?
Daniel Kaplan

1
Chỉ muốn thêm rằng nếu bạn không ở chế độ tự động cam kết, việc truyền kết nối xung quanh có thể dẫn đến tình huống một đối tượng cập nhật cơ sở dữ liệu, thì một đối tượng khác (có thể không liên quan) nhận được kết nối, có lỗi và kết thúc đẩy lùi những thay đổi của đối tượng đầu tiên. Những loại lỗi này có thể rất khó để gỡ lỗi.
TMN

2

Vượt qua các Connectiontrường hợp xung quanh thường không phải là một vấn đề, mặc dù trong hầu hết các tình huống, chỉ có các triển khai DAO nên có bất cứ điều gì để làm với chúng. Bây giờ, với vấn đề của bạn là về các kết nối không bị đóng sau khi sử dụng, thực sự rất dễ khắc phục: Connectionđối tượng cần được đóng ở cùng cấp độ được mở, tức là trong cùng một phương thức. Cá nhân tôi sử dụng mẫu mã sau:

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

Bằng cách đó, tôi đảm bảo tất cả các kết nối luôn được đóng, ngay cả khi một ngoại lệ được ném trong khối. Tôi thực sự đi miễn là sử dụng cùng một mô hình Statementvà các ResultSettrường hợp chính xác , và mọi thứ đã trôi chảy cho đến nay.

Chỉnh sửa 2018-03-29: Như được chỉ định bởi user1156544 trong các bình luận bên dưới, bắt đầu với Java 7, việc sử dụng cấu trúc try-with-resource nên được ưu tiên. Sử dụng nó, mẫu mã tôi cung cấp trong câu trả lời ban đầu của tôi có thể được đơn giản hóa như vậy:

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}

1
Tôi sử dụng một cái gì đó tương tự. Tôi có chức năng doInTransaction (tác vụ DbTask), trong đó DbTask là giao diện của tôi với phương thức với tham số kết nối. doInTransaction có được kết nối, gọi tác vụ và cam kết (hoặc khôi phục nếu có ngoại lệ) và đóng kết nối đó.
user470365

Đánh giá từ ví dụ của bạn, điều đó có nghĩa là đối tượng DataSource là một singleton?
ipohfly

@ipohfly Thật ra tôi nên đặt tên cho đối tượng dataSourceđó hơn là DataSource(Tôi sẽ sửa câu trả lời của mình về điểm đó). Loại chính xác của đối tượng đó sẽ là javax.sql.DataSource. Trong mã cũ, tôi đã từng có một đơn vị quản lý tất cả các nguồn dữ liệu có sẵn trong các ứng dụng của mình. DAO của tôi không phải biết về điều đó thông qua, vì DataSourcethể hiện được cung cấp thông qua tiêm phụ thuộc.
KevinLH

Nếu bạn sử dụng lược đồ này, hãy sử dụng thử tài nguyên tốt hơn
user1156544

Quay lại khi tôi trả lời, tôi chưa sử dụng Java 7. Nhưng bạn nói đúng rằng đây sẽ là cách ưa thích trong những ngày này. Tôi sẽ cập nhật câu trả lời của tôi để bao gồm đề xuất của bạn.
KevinLH

0

có một sự đánh đổi để làm mọi thứ theo cách này hơn là sử dụng một singleton mà bạn có thể nhận được khi cần thiết. Tôi đã làm mọi thứ cả hai trong quá khứ.

Nói chung, bạn cần suy nghĩ về hậu quả của quản lý kết nối cơ sở dữ liệu và điều này có thể hoặc không thể trực giao với việc sử dụng truy vấn cơ sở dữ liệu. Ví dụ: nếu bạn có một kết nối db cho một cá thể ứng dụng nhất định và nó bị đóng khi không sử dụng, đó sẽ là trực giao. Đặt quản lý trong một lớp đơn và không vượt qua nó. Điều này cho phép bạn quản lý kết nối db khi bạn cần. Ví dụ: nếu bạn muốn đóng một kết nối trên mỗi lần xác nhận (và mở lại trong cuộc gọi tiếp theo) thì điều này sẽ dễ thực hiện hơn trên một singleton vì API cho việc này có thể được tập trung.

Mặt khác, giả sử bạn cần quản lý một nhóm kết nối trong đó một cuộc gọi nhất định có thể cần sử dụng bất kỳ kết nối tùy ý nào. Điều này có thể xảy ra khi thực hiện các giao dịch phân tán trên nhiều máy chủ, ví dụ. Trong trường hợp này, bạn thường vượt qua đối tượng kết nối db tốt hơn nhiều so với việc bạn đang làm việc với singletons. Tôi nghĩ rằng đây thường là trường hợp hiếm hơn, nhưng không có gì sai khi làm điều đó khi bạn cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.