Дизайн БД без прохождения вокруг jdbc
У меня проблема с дизайном db, с которой я столкнулся с одним из моих проектов. Я пытаюсь реализовать службу, и часть этой службы является слоем db. Это настройка, так что у меня есть вспомогательные классы, которые выполняют методы get/update в базе данных и слой поверх них, который является janitor. Для примера:
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 все составляются несколько других обновителей, если им нужно обновлять значения в других таблицах (т.е. сохранение студента означает, что я должен коснуться таблицы классов, в которой ученик должен обновить свое последнее обновленное время, например).
Проблема, с которой я столкнулась, связана с классами обновления db, я должен передать соединение из моего класса Janitor, если я касаюсь нескольких таблиц, чтобы иметь транзакции и их возможности отката. См. Выше, что я имею в виду. Есть лучший способ сделать это? Этот тип try, catch, pass to connect to db helpers должен быть выполнен для любой операции с несколькими транзакциями в моих janitors.
Короче говоря, вы можете видеть, что код, как правило, подобен этому дублируемому по нескольким методам:
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();
}
Ответы
Ответ 1
Всякий раз, когда у вас есть дублируемая последовательность кода, но она отличается только некоторыми частями, вы можете использовать метод шаблона .
В вашем случае я бы представил класс 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;
}
}
Ответ 2
Две основные проблемы, с которыми вы сейчас сталкиваетесь, - это код плиты котла для повторяющихся задач, связанных с соединением (get/execute/close и т.д.),
и инфраструктуры для получения того же соединения через границы методов. Первый обычно решается с помощью Шаблон шаблона, а последний
используя переменные Threadlocal, чтобы передать соответствующее соединение между методами. Эти проблемы были решены в Java-мире давно, но
вам придется полагаться на фреймворк, например Spring (JDBC-шаблон) и т.д., которые имеют эту функцию за последнее десятилетие или около того, или вам нужно будет перевернуть вырезать
вниз по этой инфраструктуре. Если вы заинтересованы в последнем, вы можете сделать намек на подобные аттестаты, общие для Github, например this.