Về cơ bản, tôi đang cố gắng hiểu cách viết mã giao dịch đúng (hoặc "viết chính xác") khi phát triển dịch vụ REST với Jax-RS và Spring . Ngoài ra, chúng tôi đang sử dụng JOOQ để truy cập dữ liệu. Nhưng điều đó không liên quan lắm ...
Hãy xem xét mô hình đơn giản, nơi chúng tôi có một số tổ chức, có các trường sau: "id", "name", "code"
. Tất cả đều phải là duy nhất. Ngoài ra, có trường status
.
Tổ chức có thể bị xóa tại một số thời điểm. Nhưng chúng tôi không muốn xóa hoàn toàn dữ liệu vì chúng tôi muốn lưu dữ liệu cho mục đích phân tích/bảo trì. Vì vậy, chúng tôi chỉ đặt trường 'trạng thái' của tổ chức thành 'REMOVED'
.
Vì chúng tôi không xóa hàng tổ chức khỏi bảng, chúng tôi không thể đặt ràng buộc duy nhất vào cột "tên", bởi vì, chúng tôi có thể xóa tổ chức và sau đó tạo một tổ chức mới có cùng tên. Nhưng hãy giả sử rằng mã phải là duy nhất trên toàn cầu, vì vậy chúng tôi có một ràng buộc duy nhất trên cột code
.Cách viết mã giao dịch chính xác/đáng tin cậy với JAX-RS và Spring
Vì vậy, với điều đó, chúng ta hãy xem ví dụ đơn giản này, tạo ra tổ chức, thực hiện một số kiểm tra trên đường đi.
Resource:
@Component
@Path("/api/organizations/{organizationId: [0-9]+}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaTypeEx.APPLICATION_JSON_UTF_8)
public class OrganizationResource {
@Autowired
private OrganizationService organizationService;
@Autowired
private DtoConverter dtoConverter;
@POST
public OrganizationResponse createOrganization(@Auth Person person, CreateOrganizationRequest request) {
if (organizationService.checkOrganizationWithNameExists(request.name())) {
// this throws special Exception which is intercepted and translated to response with 409 status code
throw Responses.abortConflict("organization.nameExist", ImmutableMap.of("name", request.name()));
}
if (organizationService.checkOrganizationWithCodeExists(request.code())) {
throw Responses.abortConflict("organization.codeExists", ImmutableMap.of("code", request.code()));
}
long organizationId = organizationService.create(person.user().id(), request.name(), request.code());
return dtoConverter.from(organization.findById(organizationId));
}
}
dịch vụ DAO trông như thế:
@Transactional(DBConstants.SOME_TRANSACTION_MANAGER)
public class OrganizationServiceImpl implements OrganizationService {
@Autowired
@Qualifier(DBConstants.SOME_DSL)
protected DSLContext context;
@Override
public long create(long userId, String name, String code) {
Organization organization = new Organization(null, userId, name, code, OrganizationStatus.ACTIVE);
OrganizationRecord organizationRecord = JooqUtil.insert(context, organization, ORGANIZATION);
return organizationRecord.getId();
}
@Override
public boolean checkOrganizationWithNameExists(String name) {
return checkOrganizationExists(Tables.ORGANIZATION.NAME, name);
}
@Override
public boolean checkOrganizationWithCodeExists(String code) {
return checkOrganizationExists(Tables.ORGANIZATION.CODE, code);
}
private boolean checkOrganizationExists(TableField<OrganizationRecord, String> checkField, String checkValue) {
return context.selectCount()
.from(Tables.ORGANIZATION)
.where(checkField.eq(checkValue))
.and(Tables.ORGANIZATION.ORGANIZATION_STATUS.ne(OrganizationStatus.REMOVED))
.fetchOne(DSL.count()) > 0;
}
}
này mang lại một số câu hỏi:
- Tôi có nên đặt
@Transactional
chú thích về phương phápcreateOrganization
Resource không? Hoặc tôi có nên tạo thêm một dịch vụ nói chuyện với DAO và đặt chú thích @Transactional vào phương thức của nó không? Thứ gì khác? - Điều gì sẽ xảy ra nếu hai người dùng đồng thời gửi yêu cầu với cùng một trường
"code"
. Trước khi giao dịch đầu tiên được cam kết kiểm tra được thông qua thành công, vì vậy không có 409 respones sẽ được gửi đi. Hơn giao dịch đầu tiên sẽ được cam kết đúng, nhưng giao dịch thứ hai sẽ vi phạm ràng buộc DB. Điều này sẽ ném SQLException. Làm thế nào để xử lý một cách duyên dáng điều đó? Tôi có nghĩa là tôi vẫn muốn hiển thị thông báo lỗi tốt đẹp ở phía khách hàng, nói rằng tên đó đã được sử dụng. Nhưng tôi không thể phân tích SQLException hay smth .. tôi có thể không? - Tương tự như trang trước, nhưng lần này "tên" không phải là duy nhất. Trong trường hợp này, giao dịch thứ hai sẽ không vi phạm bất kỳ ràng buộc nào, dẫn đến việc có hai tổ chức có cùng tên, vi phạm ràng buộc kinh doanh của chúng tôi.
- Tôi có thể xem/tìm hiểu hướng dẫn/mã/v.v. ở đâu, bạn xem xét các ví dụ tuyệt vời về cách viết mã REST + DB chính xác/đáng tin cậy với logic kinh doanh phức tạp. Github/sách/blog, bất cứ điều gì. Tôi đã cố gắng để tìm một cái gì đó như myselft, nhưng hầu hết các ví dụ chỉ tập trung vào hệ thống ống nước - thêm các libs để quạ, sử dụng các chú thích, có CRUD đơn giản của bạn, kết thúc. Chúng không chứa bất kỳ sự xem xét giao dịch nào cả. I E.
CẬP NHẬT: Tôi biết về mức cách ly và thông thường error/isolation matrix (đọc bẩn, v.v.). Vấn đề tôi gặp phải là tìm một số mẫu "sẵn sàng sản xuất" để học hỏi. Hoặc một cuốn sách hay về một chủ đề. Tôi vẫn không thực sự làm thế nào để xử lý tất cả các lỗi đúng .. Tôi đoán tôi cần phải thử lại một vài lần, nếu giao dịch thất bại .. và hơn là chỉ ném một số lỗi chung và thực hiện khách hàng, mà xử lý rằng .. Nhưng làm Tôi thực sự phải sử dụng chế độ SERIALIZABLE, bất cứ khi nào tôi sử dụng các truy vấn phạm vi? Bởi vì nó sẽ ảnh hưởng đến hiệu suất rất nhiều. Nhưng nếu không thì làm thế nào tôi có thể garantee rằng giao dịch sẽ thất bại ..
Dù sao tôi đã quyết định rằng bây giờ tôi cần thêm thời gian để tìm hiểu về giao dịch và quản lý db nói chung để giải quyết vấn đề này ...
Vâng, những gì nếu có những trạng thái có giá trị khác bị đình chỉ, vv? Vấn đề là tôi không thể làm điều đó với các ràng buộc đơn giản ... Tôi có nên viết các trigger phức tạp cho mọi ràng buộc phức tạp, do đó sao chép những gì tôi đã viết trong Java? Ngoài ra làm thế nào để phục hồi một cách duyên dáng từ vi phạm những hạn chế đó? Và tôi có thể tìm thấy ví dụ điển hình về mã theo các thực tiễn đó ở đâu? –
phạm vi giao dịch là đơn vị công việc, đơn vị công trình không được xác định ở cấp DAO, nói chung có chú thích @Transactionnal ở mức DAO là mùi thiết kế – Gab
@Gab Rất không đồng ý - bạn nghĩ thông tin giao dịch ở đâu được xác định nếu không gần nơi tương tác của cơ sở dữ liệu? – Gandalf