2010-08-28 27 views
14

Tôi đã đi qua tài liệu và mã nguồn Spring và vẫn chưa tìm thấy câu trả lời cho câu hỏi của mình.Ràng buộc dữ liệu của lớp trừu tượng trong spring-mvc

Tôi có các lớp này trong mô hình miền của mình và muốn sử dụng chúng làm đối tượng biểu mẫu sao lưu trong spring-mvc.


public abstract class Credentials { 
    private Long  id; 
    .... 
} 
public class UserPasswordCredentials extends Credentials { 
    private String   username; 
    private String   password; 
    .... 
} 
public class UserAccount { 
    private Long    id; 
    private String   name; 
    private Credentials  credentials; 
    .... 
} 

điều khiển của tôi:


@Controller 
public class UserAccountController 
{ 
    @RequestMapping(value = "/saveAccount", method = RequestMethod.POST) 
    public @ResponseBody Long saveAccount(@Valid UserAccount account) 
    { 
    //persist in DB 
    return account.id; 
    } 

    @RequestMapping(value = "/listAccounts", method = RequestMethod.GET) 
    public String listAccounts() 
    { 
    //get all accounts from DB 
    return "views/list_accounts"; 
    } 
    .... 
} 

On UI Tôi có hình thức năng động cho các loại chứng chỉ khác nhau. yêu cầu POST tôi thường trông giống như:


name     name 
credentials_type  user_name 
credentials.password password 
credentials.username username 

ngoại lệ Sau đây là ném nếu tôi cố gắng đưa ra yêu cầu đến máy chủ:


org.springframework.beans.NullValueInNestedPathException: Invalid property 'credentials' of bean class [*.*.domain.UserAccount]: Could not instantiate property type [*.*.domain.Credentials] to auto-grow nested property path: java.lang.InstantiationException 
    org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:628) 

suy nghĩ ban đầu của tôi là sử dụng @ModelAttribute


    @ModelAttribute 
    public PublisherAccount prepareUserAccountBean(@RequestParam("credentials_type") String credentialsType){ 
     UserAccount userAccount = new PublisherAccount(); 
     Class credClass = //figure out correct credentials class; 
     userAccount.setCredentials(BeanUtils.instantiate(credClass)); 
     return userAccount; 
    } 

Vấn đề với phương pháp này là phương pháp prepareUserAccountBean được gọi trước bất kỳ phương thức nào khác (như listAccounts) cũng như không thích hợp.

Một giải pháp mạnh mẽ là di chuyển cả hai prepareUserAccountBeansaveUserAccount vào Bộ điều khiển riêng biệt. Nó không có vẻ đúng: Tôi muốn tất cả các hoạt động liên quan đến người dùng cư trú trong cùng một lớp điều khiển.

Bất kỳ giải pháp đơn giản nào? Tôi có thể sử dụng bằng cách nào đó DataBinder, PropertyEditor hoặc WebArgumentResolver?

Cảm ơn bạn !!!!!

+0

Bạn có thể đăng toàn bộ mã từ các lớp miền của mình không? Tôi muốn thấy các nhà thầu của bạn. –

+0

Âm thanh với tôi như thế này có nhiều vấn đề về lập bản đồ/tuần tự hóa/chuyển đổi hơn với cơ sở dữ liệu của bạn hơn là với chế độ xem. Các tài khoản được deserialized một cách chính xác vào các đối tượng chính xác trước khi được gửi đến xem? – DavidA

Trả lời

-1

Tôi không chắc chắn, nhưng bạn nên sử dụng các lớp ViewModel trên bộ điều khiển của bạn thay vì đối tượng miền. Sau đó, bên trong phương thức saveAccount của bạn, bạn sẽ xác nhận hợp lệ ViewModel này và nếu mọi thứ diễn ra đúng, bạn ánh xạ nó vào Mô hình miền của bạn và duy trì nó.

Bằng cách đó, bạn có một lợi thế khác. Nếu bạn thêm bất kỳ thuộc tính nào khác vào lớp UserAccount miền của mình, ví dụ: bool riêng làAdmin. Nếu người dùng web của bạn gửi cho bạn thông số POST với isAdmin = true sẽ liên kết với Lớp người dùng miền và tiếp tục tồn tại.

Vâng, đây là cách tôi muốn làm:

public class NewUserAccount { 
    private String name; 
    private String username; 
    private String password; 
} 

@RequestMapping(value = "/saveAccount", method = RequestMethod.POST) 
public @ResponseBody Long saveAccount(@Valid NewUserAccount account) 
{ 
    //... 
} 
+0

Nhưng nó vẫn không giải quyết vấn đề ban đầu: làm thế nào để ràng buộc một lớp kế thừa. –

3

Tôi không thể nhìn thấy bất kỳ giải pháp đơn giản và thanh lịch. Có lẽ vì vấn đề không phải là cách dữ liệu ràng buộc các lớp trừu tượng trong Spring MVC, mà là: tại sao có các lớp trừu tượng trong các đối tượng biểu mẫu ở vị trí đầu tiên? Tôi nghĩ bạn không nên.

Một đối tượng được gửi từ biểu mẫu đến bộ điều khiển được gọi là "đối tượng mẫu (sao lưu)" vì một lý do: thuộc tính đối tượng phải phản ánh các trường biểu mẫu. Nếu biểu mẫu của bạn có các trường tên người dùng và mật khẩu, thì bạn nên có các thuộc tính tên người dùng và mật khẩu trong lớp của mình.

Vì vậy, credentials phải có loại UserPasswordCredentials. Điều này sẽ bỏ qua lỗi "cố gắng khởi tạo trừu tượng" của bạn.Hai giải pháp cho việc này:

  • Khuyến cáo: bạn thay đổi kiểu của UserAccount.credentials từ Credentials để UserPasswordCredentials. Ý tôi là, Tài khoản người dùng nào có thể có, ngoại trừ UserPasswordCredentials? Hơn nữa, tôi đặt cược userAccounts cơ sở dữ liệu của bạn có một tên người dùng và mật khẩu được lưu trữ dưới dạng thông tin đăng nhập, vì vậy bạn cũng có thể có một loại UserPasswordCredentials trực tiếp trong UserAccount. Cuối cùng, Spring đề xuất sử dụng "đối tượng kinh doanh hiện tại làm đối tượng lệnh hoặc biểu mẫu" (see doc), vì vậy việc sửa đổi Tài khoản người dùng sẽ là cách để đi.
  • Không được khuyến nghị: bạn giữ UserAccount như hiện tại và bạn tạo lớp UserAccountForm. Lớp này sẽ có các thuộc tính giống như UserAccount, ngoại trừ UserAccountForm.credentials có một kiểu UserPasswordCredentials. Sau đó, khi liệt kê/lưu, một lớp (UserAccountService chẳng hạn) thực hiện chuyển đổi. Giải pháp này liên quan đến một số mã trùng lặp, do đó, chỉ sử dụng nó nếu bạn có một lý do chính đáng (các thực thể thừa kế bạn không thể thay đổi, vv).
Các vấn đề liên quan