2016-09-08 111 views
10

我有一個數據庫設計問題,我面臨着我的一個項目。我正在嘗試實現一項服務,並且該服務的一部分是一個db層。它的設置使我擁有執行數據庫獲取/更新方法的幫助類以及作爲看門人的頂層圖層。對於前:數據庫設計沒有通過jdbc

public class GetStudentDBHelper { 
    public List<Student> get(List<Integer> ids) { 
     Conn getConnection... 
     // run sql query and construct returning Student objects 
    } 
    public List<Student> get(List<Classroom> byClassroom) { 
     // get all students in passed in classrooms 
     // run sql query and construct returning Student objects 
    } 
} 

public class StudentJanitor { 
    public GetStudentDBHelper getStudentDBHelper; 
    public UpdateStudentDBHelper updateStudentDBHelper; 
    public UpdateClassroomDBHelper updateClassroomDBHelper; 

    public List<Student> getStudents(List<Integer> ids) { 
     return getStudentDBHelper.get(ids); 
    } 

    public void saveStudents(List<Students> students, int classRoomid) { 
     Connection conn = Pool.getConnection(); // assume this gives a jdbc 
     conn.autocommit(false); 

     try { 
      try 
      { 
       updateStudentDBHelper.saveForClassroom(students, classRoomid, conn); 
       updateClassroomDBHelper.markUpdated(classRoomid, conn); 
       conn.commit(); 
      } 
      catch 
      { 
       throw new MyCustomException(ErrorCode.Student); 
      } 
     } 
     catch (SQLException c) 
     { 
      conn.rollback(); 
     } 
     finally { 
      conn.close(); 
     } 
} 

public class ClassroomJanitor{ 
    public void saveClassRoon(List<Classrooms> classrooms) { 
     Connection conn = Pool.getConnection()// assume this gives a jdbc 
     conn.autocommit(false); 

     try { 

      try { 
       updateClassroomDBHelper.save(classrooms, conn); 
       updateStudentDBHelper.save(classrooms.stream().map(Classroom::getStudents).collect(Collections.toList()), conn); 
       conn.commit(); 
      } 
      catch { 
       throw new MyCustomException(ErrorCode.ClassRoom); 
      } 
     } 
     catch (SQLException c) 
     { 
      conn.rollback(); 
     } 
     finally { 
      conn.close(); 
     } 
}... 

public class GetClassroomDBHelper{}... 
public class UpdateClassroomDBHelper{}... 

更新DB類的所有構成的情況下,他們需要更新其它表(值即節省了學生意味着我必須觸摸其中一個學生屬於更新教室表中的多個其他updators。它的最後更新時間)。

我遇到的問題是更新db類,我必須從我的Janitor類傳遞一個連接,如果我觸摸多個表以獲得事務及其回滾功能。請參閱上文,瞭解我的意思。有一個更好的方法嗎?這種類型的try,catch,將conn傳遞給db helper,必須在我的管理員中進行任何多事務操作。

總之,你可以看到,代碼通常是這樣的跨多種方法複製:目前你所面對

 Connection conn = Pool.getConnection()// assume this gives a jdbc 
     conn.autocommit(false); 

     try { 

      try { 
       //do some business logic requiring Connection conn 
      } 
      catch { 
       throw new MyCustomException(ErrorCode); 
      } 
     } 
     catch (SQLException c) 
     { 
      conn.rollback(); 
     } 
     finally { 
      conn.close(); 
     } 
+3

好了,你可以有一個方法,做在try-catch回滾提交事情,並且你傳遞一個'Runnable'或類似的函數接口,它調用任何需要連接的方法。但是,爲什麼不使用Hibernate來管理數據庫和邏輯呢? – RealSkeptic

+0

@RealSkeptic我在我的數據庫基本上只有原始連接(只是一個要求)有限。你能舉出一個如何實現的例子代碼嗎? –

+0

我認爲使用模板設計模式可以提高代碼的可讀性並避免使用樣板 – Koitoer

回答

2

兩個主要問題是針對與連接重複性任務的鍋爐板代碼(獲取/執行/關閉等) 以及跨方法邊界獲取相同連接的基礎結構。第一個典型地通過使用Template pattern和後者 使用Threadlocal變量來解決跨過跨方法的適當連接。這些類型的問題早已在Java世界中解決了,但 將要求您依賴像Spring這樣的框架(JDBC template)等,它具有上個十年左右的功能,或者您需要推出剝離的 這個基礎結構的版本。如果你對後者感興趣,那麼你可以從Github上共享的類似attmepts中得到提示,如this

+1

爲什麼這會被降低?這是正確的答案,但它只是缺少一個可以通過鏈接找到的例子嗎? –

3

每當你有一個重複的代碼序列,但它只在某些部分不同,你可以使用template method

在你的情況下,我會引入一個TransactionTemplate類,併爲不同的零件使用回調接口。例如。

public class TransactionTemplate { 

    private DataSource dataSource; 

    public TransactionTemplate(DataSource dataSource) { 
     this.dataSource = Objects.requireNonNull(dataSource); 

    } 

    public <T> T execute(TransactionCallback<T> transactionCallback) throws Exception { 
     Connection conn = dataSource.getConnection();// assume this gives a jdbc 
     try { 
      conn.setAutoCommit(false); 
      T result = transactionCallback.doInTransaction(conn); 
      conn.commit(); 
      return result; 
     } catch (Exception e) { 
      conn.rollback(); 
      throw e; 
     } finally { 
      conn.close(); 
     } 
    } 
} 

回調接口看起來像這樣

public interface TransactionCallback<T> { 
    public T doInTransaction(Connection conn) throws Exception; 
} 

正如你可以看到TransactionTemplate管理事務,而TransactionCallback實現必須在一個事務中完成的邏輯。然後

你的客戶端代碼看起來像這樣

public class StudentJanitor { 

    private TransactionTemplate transactionTemplate; 

    StudentJanitor(DataSource dataSource) { 
     transactionTemplate = new TransactionTemplate(dataSource); 
    } 

    public void saveStudents(List<Students> students, int classRoomid) { 
     SaveStudentsTransaction saveStudentsTransaction = new SaveStudentsTransaction(students, classRoomid); 
     transactionTemplate.execute(saveStudentsTransaction); 
    } 

} 

和邏輯放置在TransactionCallback

public class SaveStudentsTransaction implements TransactionCallback<Void> { 

    public GetStudentDBHelper getStudentDBHelper; 
    public UpdateStudentDBHelper updateStudentDBHelper; 
    public UpdateClassroomDBHelper updateClassroomDBHelper; 

    private List<Students> students; 
    private int classRoomid; 

    public SaveStudentsTransaction(List<Students> students, int classRoomid) { 
     this.students = students; 
     this.classRoomid = classRoomid; 
    } 

    @Override 
     public Void doInTransaction(Connection conn) throws Exception { 
      try 
      { 
       updateStudentDBHelper.saveForClassroom(students, classRoomid, conn); 
       updateClassroomDBHelper.markUpdated(classRoomid, conn); 
       conn.commit(); 
      } 
      catch 
      { 
       throw new MyCustomException(ErrorCode.Student); 
      } 
      return null; 
     } 

} 
+0

因此,如果我正確地遵循這一點,我需要爲每個我希望使用事務回調執行的操作分別創建一個類。即。我需要爲UpdateStudentsTransaction和DeleteStudentsTransactions等整個其他類。這將得到超級詳細的真正快速。 –