怎么做网站相关关键词,网址创作,网络服务提供者不得在什么时间,传奇手游代理平台价格表最近#xff0c;我惊讶于一个代码库在其所有域实体中都具有公共默认构造函数#xff08;即零参数构造函数#xff09;#xff0c;并且所有字段都具有getter和setter。 当我深入研究时#xff0c;我发现域实体之所以如此#xff0c;主要是因为该团队认为Web / MVC框架需要… 最近我惊讶于一个代码库在其所有域实体中都具有公共默认构造函数即零参数构造函数并且所有字段都具有getter和setter。 当我深入研究时我发现域实体之所以如此主要是因为该团队认为Web / MVC框架需要它。 我认为这是消除一些误解的好机会。 具体来说我们将研究以下情况 生成的ID字段没有设置器即生成的ID字段具有吸气剂但没有设置器 没有默认的构造函数例如没有公共的零参数构造函数 具有子实体的域实体例如子实体未显示为可修改列表 绑定Web请求参数 首先一些细节和背景。 让我们基于特定的Web / MVC框架-Spring MVC。 使用Spring MVC时其数据绑定按名称绑定请求参数。 让我们举个例子。 Controller
RequestMapping(/accounts)
... class ... {...PostMappingpublic ... save(ModelAttribute Account account, ...) {...}...
} 给定上面的控制器映射到“ / accounts”一个Account实例可以从哪里来 根据文档 Spring MVC将使用以下选项获取实例 从模型如果已通过Model添加例如通过同一控制器中的ModelAttribute方法 。 通过SessionAttributes在HTTP会话中。 来自通过Converter的URI路径变量。 从默认构造函数的调用开始。 仅适用于Kotlin通过调用具有与Servlet请求参数匹配的参数的“主要构造函数” 参数名称是通过JavaBeans ConstructorProperties或字节码中运行时保留的参数名称确定的。 假设没有在会话中添加Account对象并且没有ModelAttribute方法 Spring MVC最终将使用其默认构造函数实例化一个实例并按name绑定Web请求参数。 例如请求包含“ id”和“ name”参数。 Spring MVC将尝试通过分别调用“ setId”和“ setName”方法将它们绑定到“ id”和“ name” bean属性。 这遵循JavaBean约定。 生成ID字段的无设置方法 让我们从简单的事情开始。 假设我们有一个Account域实体。 它具有由持久性存储生成的ID字段并且仅提供getter方法但不提供setter方法。 Entity
... class Account {Id GeneratedValue(...) private Long id;...public Account() { ... }public Long getId() { return id; }// but no setId() method
} 那么我们如何让Spring MVC将请求参数绑定到Account域实体 我们是否必须为生成的字段和只读字段提供公共设置方法 在我们HTML表单中我们不会将“ id”作为请求参数。 我们将其放置为路径变量。 我们使用ModelAttribute方法。 在请求处理方法之前调用它。 它支持与常规请求处理方法几乎相同的参数。 在我们的例子中我们使用它来检索具有给定唯一标识符的Account域实体并将其用于进一步的绑定。 我们的控制器看起来像这样。 Controller
RequestMapping(/accounts)
... class ... {...ModelAttributepublic Account populateModel(HttpMethod httpMethod,PathVariable(requiredfalse) Long id) {if (id ! null) {return accountRepository.findById(id).orElseThrow(...);}if (httpMethod HttpMethod.POST) {return new Account();}return null;}PutMapping(/{id})public ... update(...,ModelAttribute Valid Account account, ...) {...accountRepository.save(account);return ...;}PostMappingpublic ... save(ModelAttribute Valid Account account, ...) {...accountRepository.save(account);return ...;}...
} 更新现有帐户时请求将是对“ / accounts / {id}” URI的PUT 。 在这种情况下我们的控制器需要检索具有给定唯一标识符的域实体并向Spring MVC提供相同的域对象以进行进一步绑定如果有。 “ id”字段将不需要设置方法。 添加或保存新帐户时请求将是“ / accounts”的POST 。 在这种情况下我们的控制器需要使用一些请求参数创建一个新的域实体并向Spring MVC提供相同的域对象以进行进一步绑定如果有。 对于新的域实体“ id”字段保留为null 。 基础的持久性基础结构将在存储时生成一个值。 尽管如此“ id”字段仍不需要设置方法。 在这两种情况下 ModelAttribute方法populateModel均在映射的请求处理方法之前被调用。 因此我们需要在populateModel使用参数来确定在哪种情况下使用它。 域对象中没有默认构造函数 假设我们的Account域实体没有提供默认构造函数即没有零参数构造函数。 ... class Account {public Account(String name) {...}...// no public default constructor// (i.e. no public zero-arguments constructor)
} 那么我们如何让Spring MVC将请求参数绑定到Account域实体 它不提供默认的构造函数。 我们可以使用ModelAttribute方法。 在这种情况下我们要创建一个带有请求参数的Account域实体并将其用于进一步的绑定。 我们的控制器看起来像这样。 Controller
RequestMapping(/accounts)
... class ... {...ModelAttributepublic Account populateModel(HttpMethod httpMethod,PathVariable(requiredfalse) Long id,RequestParam(requiredfalse) String name) {if (id ! null) {return accountRepository.findById(id).orElseThrow(...);}if (httpMethod HttpMethod.POST) {return new Account(name);}return null;}PutMapping(/{id})public ... update(...,ModelAttribute Valid Account account, ...) {...accountRepository.save(account);return ...;}PostMappingpublic ... save(ModelAttribute Valid Account account, ...) {...accountRepository.save(account);return ...;}...
}具有子实体的域实体 现在让我们看一下具有子实体的域实体。 这样的东西。 ... class Order {private Map..., OrderItem items;public Order() {...}public void addItem(int quantity, ...) {...}...public CollectionCartItem getItems() {return Collections.unmodifiableCollection(items.values());}
}... class OrderItem {private int quantity;// no public default constructor...
} 请注意订单中的项目不会显示为可修改列表。 Spring MVC支持索引属性并将它们绑定到数组列表或其他自然排序的集合。 但是在这种情况下 getItems方法将返回无法修改的集合。 这意味着当对象尝试向其添加/删除项目时将引发异常。 那么如何让Spring MVC将请求参数绑定到Order域实体 我们是否被迫将订单项公开为可变列表 并不是的。 我们必须避免用表示层关注点来稀释域模型例如Spring MVC。 相反我们使表示层成为域模型的客户端。 为了处理这种情况我们创建了另一个符合Spring MVC的类型并使我们的域实体与表示层无关。 ... class OrderForm {public static OrderForm fromDomainEntity(Order order) {...}...// public default constructor// (i.e. public zero-arguments constructor)private ListOrderFormItem items;public ListOrderFormItem getItems() { return items; }public void setItems(ListOrderFormItem items) { this.items items; }public Order toDomainEntity() {...}
}... class OrderFormItem {...private int quantity;// public default constructor// (i.e. public zero-arguments constructor)// public getters and setters
} 请注意完全可以创建一个了解域实体的表示层类型。 但是让域实体知道表示层对象并不是全部。 更具体地说表示层OrderForm知道Order域实体。 但是Order不了解表示层OrderForm 。 这是我们的控制器的外观。 Controller
RequestMapping(/orders)
... class ... {...ModelAttributepublic OrderForm populateModel(HttpMethod httpMethod,PathVariable(requiredfalse) Long id,RequestParam(requiredfalse) String name) {if (id ! null) {return OrderForm.fromDomainEntity(orderRepository.findById(id).orElseThrow(...));}if (httpMethod HttpMethod.POST) {return new OrderForm(); // new Order()}return null;}PutMapping(/{id})public ... update(...,ModelAttribute Valid OrderForm orderForm, ...) {...orderRepository.save(orderForm.toDomainEntity());return ...;}PostMappingpublic ... save(ModelAttribute Valid OrderForm orderForm, ...) {...orderRepository.save(orderForm.toDomainEntity());return ...;}...
}总结思想 正如我在之前的文章中提到的可以让您的域对象看起来像具有公共默认零参数构造函数getter和setter的JavaBean。 但是如果域逻辑开始变得复杂并且要求某些域对象失去其JavaBean风格例如不再有公共的零参数构造函数没有更多的setter则不必担心。 定义新的JavaBean类型以满足与表示相关的问题。 不要稀释域逻辑。 目前为止就这样了。 我希望这有帮助。 再次感谢Juno帮助我提供样品。 相关代码段可以在GitHub上找到 。 翻译自: https://www.javacodegeeks.com/2018/06/domain-objects-spring-mvc.html