데이터베이스 연결-매개 변수로 전달되어야합니까?


11

우리는 공통 메소드를 사용하여 데이터베이스 연결을 한 번 가져오고 사용할 관련 클래스를 통해 전달하는 시스템을 가지고 있습니다. 데이터베이스 연결을 매개 변수로 다른 클래스에 전달하면 문제가 발생할 것이라는 의심이 있습니다. 실제로 이것이 가능한지 여부를 확인하고 더 나은 패턴이 있습니까?

지속성을 수행하는 ORM 도구가 있다는 것을 알고 있지만 아직 그럴 수는 없습니다.

모든 의견을 환영합니다.


어떤 종류의 문제를 언급하고 있습니까? 누가 이런 의심이 있습니까? (아니 당신은 내가 가정합니다.)
그렉 Hewgill

1
개발자가 연결을 닫는 것을 잊게하는 것과 같은 문제는 일반적으로 데이터베이스 연결을 매개 변수로 다양한 방법으로 전달하는 것이 좋은 방법인지 확인하려고합니다. 나중에 다른 개발자로부터 의심이옵니다.
ipohfly

답변:


8

예, 연결을 통과하는 것이 안전합니다. 외부 제어 블록에서 연결을 처리합니다. 안전하지 않은 것은 없습니다.

안전하지 않은 것은 연결이 적시에 올바르게 처리되도록 보장하지 않는 코드를 작성하는 것입니다. 리소스 정리를 잊어 버리는 것은 리소스를 전달하는 것과 관련이 없습니다. 어디서나 전달하지 않고 연결을 끊는 코드를 쉽게 작성할 수 있습니다.

C ++에서는 스택에 할당하거나 스마트 포인터를 사용하면 RAII로 보호됩니다. C #에서는 모든 일회용 개체 (예 : 연결)가 "using"블록에 선언된다는 엄격한 규칙을 따릅니다. Java에서는 try-finally 로직으로 정리하십시오. 이를 보장하기 위해 모든 데이터 계층 코드에 대한 코드 검토를 수행하십시오.

가장 일반적인 사용 사례는 여러 순열로 결합 할 수있는 여러 작업이있는 경우입니다. 그리고 이러한 순열 각각은 원자 트랜잭션이어야합니다 (모두 성공 또는 롤백). 그런 다음 모든 메소드에 트랜잭션 (및 해당 연결)을 전달해야합니다.

원자 트랜잭션으로 다양한 방법으로 결합 할 수있는 많은 foobar () 액션이 있다고 가정합니다.

//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 연결을 가능한 한 늦게 열고 가능한 빨리 폐기하십시오. 연결을 객체 구성원으로 취급하고 연결을 불필요한 상태로 도입하고 연결을 필요 이상으로 길게 열면 팀원이 옳을 수 있습니다. 그러나 연결 또는 트랜잭션을 매개 변수로 전달하는 행위는 본질적으로 잘못되지 않습니다.

BTW. 퍼스트 클래스 함수에 대한 언어의 지원에 따라 foobar () 액션 목록에서 취할 수 있습니다. 따라서 하나의 함수가 동작의 모든 순열을 처리 할 수 ​​있습니다. 각 순열에 대한 외부 제어 블록의 중복 제거.


상황이
어떤지

6

Dependency Injection 후에있는 것처럼 들립니다 . 즉, 풀링 된 연결이 한 번 생성되어 필요할 때마다 주입됩니다. 메소드 매개 변수를 통해 연결을 전달하는 것은 의존성 주입 방법 중 하나이지만 Guice, PicoContainer 또는 Spring과 같은 IoC 컨테이너는 다른 방법으로 수행 할 수 있습니다.

DI를 사용하면 핵심 비즈니스 로직과는 별도로 연결 생성, 열기, 사용 및 닫기에 대한 로직을 깔끔하게 마무리 할 수 ​​있습니다.

Spring JDBC et al은 이러한 종류의 동작을 수행하는 예제입니다.


실제로 의존성 주입을보고 있지는 않습니다. 일반적으로 그렇게하는 것이 좋은 습관인지 아닌지 알아 내려고 시도하고 그렇지 않은 경우 데이터베이스 연결을 관리하는 더 좋은 방법은 무엇입니까?
ipohfly

-1. 하나의 연결은 다중 사용자 시스템에 맞지 않습니다. 사용자 수가 적고 실행 속도가 빠르기 때문에 작동하는 것처럼 보일 수 있습니다. 풀링을 사용하면 단일 사용자 시스템에서도 작업 별 연결 개체를 인스턴스화하는 것이 좋습니다.
mike30

2

데이터가 아닌 데이터베이스를 전달하면 문제가 발생할 수 있습니다. 그 정도까지는 실제 데이터베이스 위생을 보장 할 수 없다면 데이터베이스를 전달하지 마십시오.

데이터베이스를 전달할 때의 문제점은 조잡 할 수 있다는 것입니다. 누군가 데이터베이스 연결을 통과하는 코드에서 둘 이상의 버그를 보았습니다. 누군가가 결과 집합을 가져 와서 로컬 개체 (결과 집합은 여전히 ​​데이터베이스에 연결되어 있음)에 고정시킨 다음 커서를 연결합니다. 상당한 시간 동안 데이터베이스. 다른 인스턴스가 다른 사람에게 결과 세트를 전달한 다음 (결과가 스태킹 된) 결과 세트를 전달한 메소드가 결과를 닫고 (및 명령문) 다른 메소드가 더 이상 그렇지 않은 결과 세트로 작업을 시도하면 오류가 발생합니다.

이 모든 것은 데이터베이스, 연결, 명령문, 결과 세트 및 라이프 사이클을 존중하지 않기 때문입니다.

이를 피하기 위해 데이터베이스와 더 잘 작동하는 기존 패턴과 구조가 있으며, 제한된 클래스에서 데이터베이스를 가져와야 할 필요가 없습니다. 데이터가 들어오고, 데이터가 나가고, 데이터베이스가 그대로 유지됩니다.


1
+1 db 연결은 가능한 가장 짧은 시간 간격을 가져야합니다. 그것을 열고 사용하고 가능한 빨리 닫으십시오. 요즘에는 많은 연결 풀 구현이 있으므로 여러 작업에 대한 연결을 사용하면 잘못된 경제가 발생합니다. 버그 또는 성능 문제에 대한 초대 (연결 리소스를 사용하여 테이블 잠금 유지)
jqa

이러한 기존 패턴과 구조 중 일부의 이름은 무엇입니까?
Daniel Kaplan

@tieTYT 최전방에 오는 주요한 것은 데이터베이스의 나머지 응용 프로그램에서 데이터베이스를 숨기는 역할을 하는 데이터 액세스 객체 입니다. 데이터 액세스 계층객체 관계형 매핑

그 패턴들을 생각할 때 나는 그들이 요구하는 것보다 더 높은 추상화 수준에 있다고 느낍니다. RootDao에서 얻을 수있는 방법이 있다고 가정 해 봅시다 . 그러나 당신은 당신 이 그것으로 Node전체 Root물건을 꺼내지 않고 얻을 수있는 방법을 원한다는 것을 알고 있습니다 . RootDao가 NodeDao 코드 (예 : 재사용)를 호출 하도록하려면 어떻게해야합니까? 그러나 Dao가 직접 호출 Node될 때 Dao 만 연결을 닫고 Dao가 호출 Node될 때 연결을 유지해야 Root합니까?
Daniel Kaplan

1
자동 커밋 모드가 아닌 경우 연결을 전달하면 한 개체가 데이터베이스를 업데이트하고 다른 개체 (연결되지 않은 개체)가 연결을 가져오고 오류가 발생하는 상황이 발생할 수 있습니다. 첫 번째 객체의 변경 사항을 롤백합니다. 이러한 유형의 오류는 디버깅하기 가 매우 어려울 수 있습니다 .
TMN

2

Connection대부분의 상황에서 DAO 구현 만 관련이 있지만 인스턴스를 전달 하는 것은 일반적으로 문제가되지 않습니다. 이제 사용 후 연결이 닫히지 않는 문제에 대한 문제로 인해 실제로 수정하기가 쉽습니다. Connection개체가 열려있는 것과 같은 수준, 즉 동일한 방법으로 개체를 닫아야합니다. 개인적으로 다음 코드 패턴을 사용합니다.

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

그렇게하면 블록 내에서 예외가 발생하더라도 모든 연결이 항상 닫힙니다. 나는 실제로 동일한 패턴 StatementResultSet인스턴스 를 사용하는 한 계속 가고 있으며 , 지금까지 모든 것이 순조롭게 진행되었습니다.

2018-03-29 편집 : 아래 주석에서 user1156544에 표시된 것처럼 Java 7부터 try-with-resources 구문을 사용하는 것이 좋습니다. 이를 사용하여 초기 답변에서 제공 한 코드 패턴을 다음과 같이 단순화 할 수 있습니다.

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

1
나는 비슷한 것을 사용합니다. doInTransaction (DbTask task) 함수가 있는데 DbTask는 연결 매개 변수가있는 메소드가있는 인터페이스입니다. doInTransaction은 연결을 확보하고 태스크를 호출하고 커밋 (또는 예외가있는 경우 롤백)하고 해당 연결을 닫습니다.
user470365

귀하의 예에서 판단하면 DataSource 객체가 싱글 톤입니까?
ipohfly

@ipohfly 사실 실제로는 그 객체의 이름을 지정하는 dataSource것이 아니라 그 객체의 이름을 지정해야합니다 DataSource. 해당 객체의 정확한 유형은입니다 javax.sql.DataSource. 이전 코드에서는 단일 응용 프로그램 내에서 사용 가능한 모든 데이터 소스를 싱글 톤으로 관리했습니다. DataSource인스턴스가 의존성 주입을 통해 제공되므로 DAO는이를 통해 알 필요가 없습니다 .
KevinLH

이 스키마를 사용하면 try-with-resources를 더 잘 사용하십시오
user1156544

내가 대답했을 때, 나는 아직 Java 7을 사용하지 않았습니다. 그러나 요즘에는 이것이 선호되는 방법이어야합니다. 귀하의 제안을 포함하도록 답변을 업데이트하겠습니다.
KevinLH

0

필요에 따라 얻을 수있는 싱글 톤을 사용하는 대신 이러한 방식으로 작업을 수행하는 것이 절충됩니다. 나는 과거에 두 가지 일을 해왔다.

일반적으로 데이터베이스 연결 관리의 결과에 대해 생각해야하며 이는 데이터베이스 쿼리 사용과 직교하거나 아닐 수 있습니다. 예를 들어, 주어진 애플리케이션 인스턴스에 대해 하나의 db 연결이 있고 사용하지 않을 때 닫히면 직교하는 것입니다. 경영진을 싱글턴 클래스에 배치하고 통과시키지 마십시오. 이를 통해 필요에 따라 DB 연결을 관리 할 수 ​​있습니다. 예를 들어, 모든 커밋에서 연결을 닫고 다음 호출에서 다시 열려면이를위한 API를 중앙 집중화 할 수 있기 때문에 싱글 톤에서 수행하는 것이 더 쉽습니다.

반면에, 주어진 통화가 임의의 연결을 사용해야하는 연결 풀을 관리해야한다고 가정하십시오. 예를 들어 여러 서버에서 분산 트랜잭션을 수행 할 때 이런 일이 발생할 수 있습니다. 이 경우 일반적으로 싱글 톤을 사용하는 것보다 db 연결 객체를 전달하는 것이 훨씬 좋습니다. 나는 이것이 가장 드문 경우라고 생각하지만 필요할 때 그것을하는 데 아무런 문제가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.