java - 不绕过 jdbc 的数据库设计

标签 java design-patterns jdbc java-8

我的一个项目遇到了数据库设计问题。我正在尝试实现一项服务,该服务的一部分是数据库层。它的设置使得我有帮助程序类对数据库执行 get/update 方法,并且在它们之上有一个看门人层。例如:

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{}...

更新数据库类都包含多个其他更新器,以防他们需要更新其他表中的值(即,保存学生意味着我必须触摸学生所属的教室表以更新其最后更新时间) .

我遇到的问题是更新数据库类,如果我要接触多个表以获得事务及其回滚功能,我必须从我的 Janitor 类传递一个连接。见上文我的意思。有一个更好的方法吗?这种类型的尝试、捕获、将 conn 传递给数据库助手,必须在我的看门人中完成任何多事务操作。

简而言之,您可以看到代码通常是这样的,跨多个方法重复:

       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();
       }

最佳答案

每当你有一个重复的代码序列,但它只是在某些部分不同时,你可以使用 template method .

在您的情况下,我会引入一个 TransactionTemplate 类,并对不同的部分使用回调接口(interface)。例如

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();
        }
    }
}

回调接口(interface)看起来像这样

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;
        }

}

关于java - 不绕过 jdbc 的数据库设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39391991/

相关文章:

java - 对于 minOccurs ="0"的元素到 Java 的 XSD 转换与默认出现的元素不同

java - 如何订阅 MQTT 主题并在 Eclipse (Java) 上打印收到的消息

reactjs - 为什么在 Material-UI 的 SwipeableDrawer 中将 onClose 和 onOpen 标记为 required?

java - 如何处理装饰器模式中的 'this' 引用

design-patterns - 使用 jUnit 进行 DAO 测试的设计模式

java - 在 JDBC 中将密码哈希转换为密码

java - 发行文件 Assets Libgdx

java - 使用 jPBC 在 java 中实现双线性配对

java - log4j2 JDBC Appender 问题 :Failed to Insert Record

mysql - 通过 JDBC 执行 INSERT IGNORE 时获取重复计数