mvc框架springmvc 通用mapperr怎么传参的

SpringMVC整合MyBatis(51)
集合类型绑定
1.数组绑定
商品批量删除,用户在页面选择多个商品,批量删除。
1.2表现层实现
关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
controller方法定义:
//批量删除商品的信息
//批量删除商品的信息
@RequestMapping(&/deleteItems&)
public String deleteItems(int[] items_id)throws Exception{
//调用Service批量删除商品
itemsService.detleteItemsById(items_id);
return &success&;
itemsServiceimpl:
public void detleteItemsById(int[] id) throws Exception {
for (int i = 0; i & id. i++) {
System.out.println(&id[&+i+&]=&+id[i]);
//由于items拥有外键,删除方法暂时不能使用
//itemsMapper.deleteByPrimaryKey(id[i]);
页面定义:(加&批量删除按钮和CheckBox多选框&以及查询和删除按钮的JS方法)
&%@ page language=&java& contentType=&text/ charset=UTF-8&
pageEncoding=&UTF-8&%&
&%@ taglib uri=&/jsp/jstl/core& prefix=&c& %&
&%@ taglib uri=&/jsp/jstl/fmt&
prefix=&fmt&%&
&!DOCTYPE html PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN& &http://www.w3.org/TR/html4/loose.dtd&&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&title&查询商品列表&/title&
&script type=&text/javascript&&
function deleteItems(){
//提交form
document.itemsForm.action=&${pageContext.request.contextPath }/items/deleteItems.action&;
document.itemsForm.submit();
function queryItems(){
//提交form
document.itemsForm.action=&${pageContext.request.contextPath }/items/queryItems.action&;
document.itemsForm.submit();
&form name=&itemsForm& action=&${pageContext.request.contextPath }/items/queryItems.action& method=&post&&
查询条件:
&table width=&100%& border=1&
&td&商品名称:&input name=&itemsCustom.name&/&&/td&
&td&&input type=&button& value=&查询& onclick=&queryItems()&/&
&input type=&button& value=&批量删除& onclick=&deleteItems()&/&
商品列表:
&table width=&100%& border=1&
&td&选择&/td&
&td&商品名称&/td&
&td&商品价格&/td&
&td&生产日期&/td&
&td&商品描述&/td&
&td&操作&/td&
&c:forEach items=&${itemsList }& var=&item&&
&td&&input type=&checkbox& name=&items_id& value=&${item.id}&/&&/td&
&td&${item.name }&/td&
&td&${item.price }&/td&
&td&&fmt:formatDate value=&${item.createtime}& pattern=&yyyy-MM-dd HH:mm:ss&/&&/td&
&td&${item.detail }&/td&
&td&&a href=&${pageContext.request.contextPath }/items/editItems.action?id=${item.id}&&修改&/a&&/td&
&/c:forEach&
选择了3,4,6商品,如图
id传值成功,但是由于items拥有外键,删除方法暂时不能使用
2.list绑定
通常在需要批量提交数据时,将提交的数据绑定到list&pojo&中,比如:成绩录入(录入多门课成绩,批量提交),
本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
2.2表现层实现
controller方法定义:
1、进入批量商品修改页面(页面样式参考商品列表实现)
2、批量修改商品提交
使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list&pojo&属性
package cn.edu.hpu.ssm.
import java.util.L
//商品查询包装对象
public class ItemsQueryVo {
//商品信息包装
//为了系统可拓展性,对原始生成的PO进行拓展
private ItemsCustom itemsC
//批量商品信息
private List&ItemsCustom& itemsL
//get和set方法省略
Controller定义:
//批量修改商品的页面,将商品信息查询出来,在页面中可以商品信息
@RequestMapping(&/editItemsQuery&)
public ModelAndView editItemsQuery(HttpServletRequest request,ItemsQueryVo itemsQueryVo)throws Exception{
//调用Service查找数据库,查询商品列表,这里使用静态数据模拟
List&ItemsCustom& itemsList=itemsService.findItemsList(itemsQueryVo);
//返回ModelAndView
ModelAndView modelAndView=new ModelAndView();
//相当于request的setAttribut,在jsp页面中通过这个来取数据
modelAndView.addObject(&itemsList&,itemsList);
modelAndView.setViewName(&items/editItemsQuery&);
return modelAndV
//批量修改商品的提交
//通过ItemsQueryVo接受批量提交的商品信息,将商品信息存储到ItemsQueryVo中itemsList属性中
@RequestMapping(&/editItemsAllSubmit&)
public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo)throws Exception{
//暂且不执行更新,我们只是测试list数据的传输
System.out.println(&传进来的list的大小=&+itemsQueryVo.getItemsList().size());
return &success&;
页面定义(editItemsQuery.jsp):
&%@ page language=&java& contentType=&text/ charset=UTF-8&
pageEncoding=&UTF-8&%&
&%@ taglib uri=&/jsp/jstl/core& prefix=&c& %&
&%@ taglib uri=&/jsp/jstl/fmt&
prefix=&fmt&%&
&!DOCTYPE html PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN& &http://www.w3.org/TR/html4/loose.dtd&&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&title&查询商品列表&/title&
&script type=&text/javascript&&
function editItemsAllSubmit(){
//提交form
document.itemsForm.action=&${pageContext.request.contextPath }/items/editItemsAllSubmit.action&;
document.itemsForm.submit();
function queryItems(){
//提交form
document.itemsForm.action=&${pageContext.request.contextPath }/items/queryItems.action&;
document.itemsForm.submit();
&form name=&itemsForm& action=&${pageContext.request.contextPath }/items/queryItems.action& method=&post&&
查询条件:
&table width=&100%& border=1&
&td&商品名称:&input name=&itemsCustom.name&/&&/td&
&td&&input type=&button& value=&查询& onclick=&queryItems()&/&
&input type=&button& value=&批量修改提交& onclick=&editItemsAllSubmit()&/&
商品列表:
&table width=&100%& border=1&
&td&商品名称&/td&
&td&商品价格&/td&
&td&生产日期&/td&
&td&商品描述&/td&
&td&操作&/td&
&c:forEach items=&${itemsList }& var=&item& varStatus=&status&&
&td&&input name=&itemsList[${status.index }].name& value=&${item.name }&/&&/td&
&td&&input name=&itemsList[${status.index }].price& value=&${item.price }&/&&/td&
&td&&input name=&itemsList[${status.index }].createtime& value=&&fmt:formatDate value=&${item.createtime}& pattern=&yyyy-MM-dd HH:mm:ss&/&&/&&/td&
&td&&input name=&itemsList[${status.index }].detail& value=&${item.detail }&/&&/td&
&/c:forEach&
测试,首先打开批量修改界面,如图
看看编辑处的源码:
&td&&input name=&itemsList[0].name& value=&电视机&/&&/td&
&td&&input name=&itemsList[0].price& value=&2699.0&/&&/td&
&td&&input name=&itemsList[0].createtime& value=& 00:00:00&/&&/td&
&td&&input name=&itemsList[0].detail& value=&等离子电视机,液晶屏幕&/&&/td&
&td&&input name=&itemsList[1].name& value=&洗衣机&/&&/td&
&td&&input name=&itemsList[1].price& value=&1300.0&/&&/td&
&td&&input name=&itemsList[1].createtime& value=& 21:15:12&/&&/td&
&td&&input name=&itemsList[1].detail& value=&双滚筒洗衣机,马力十足&/&&/td&
&td&&input name=&itemsList[2].name& value=&电冰箱&/&&/td&
&td&&input name=&itemsList[2].price& value=&3200.0&/&&/td&
&td&&input name=&itemsList[2].createtime& value=& 09:57:30&/&&/td&
&td&&input name=&itemsList[2].detail& value=&双拉门电冰箱,智能保鲜功能&/&&/td&
&td&&input name=&itemsList[3].name& value=&iPhone-5S&/&&/td&
&td&&input name=&itemsList[3].price& value=&3999.0&/&&/td&
&td&&input name=&itemsList[3].createtime& value=& 00:00:00&/&&/td&
&td&&input name=&itemsList[3].detail& value=&正品行货&/&&/td&
&td&&input name=&itemsList[4].name& value=&微波炉&/&&/td&
&td&&input name=&itemsList[4].price& value=&2500.0&/&&/td&
&td&&input name=&itemsList[4].createtime& value=& 11:38:14&/&&/td&
&td&&input name=&itemsList[4].detail& value=&加热快,微波更安全!&/&&/td&
&td&&input name=&itemsList[5].name& value=&榨汁机&/&&/td&
&td&&input name=&itemsList[5].price& value=&700.0&/&&/td&
&td&&input name=&itemsList[5].createtime& value=& 11:38:51&/&&/td&
&td&&input name=&itemsList[5].detail& value=&双马力榨汁,更快色,更新鲜&/&&/td&
我们点击批量修改,可以看到
list数据传输测试成功!
也通过在包装pojo中定义map类型属性。
在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。
包装类中定义Map对象如下:
Public class QueryVo {
private Map&String, Object& itemInfo = new HashMap&String, Object&();
//get/set方法..
页面定义如下:
&td&学生信息:&/td&
姓名:&inputtype=&text&name=&itemInfo['name']&/&
年龄:&inputtype=&text&name=&itemInfo['price']&/&
Contrller方法定义如下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
其他类型的我们可以自己简单的测试一下
转载请注明出处:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:350977次
积分:10399
积分:10399
排名:第1015名
原创:704篇
转载:18篇
评论:196条
就职:北京科蓝软件上海分公司
职位:软件开发工程师
负责:网银系统开发与维护
专业:软件工程12级
阅读:1383
阅读:2004
文章:35篇
阅读:23898
文章:27篇
阅读:7944
文章:24篇
阅读:7453
文章:81篇
阅读:58013
文章:87篇
阅读:37644
文章:21篇
阅读:8747
文章:51篇
阅读:16647
文章:37篇
阅读:14733
文章:31篇
阅读:36125  经过EF的《》,我们已经把数据访问层基本搭建起来了,但并没有涉及实体关系。实体关系对于一个数据库系统来说至关重要,而且EF的各个实体之间的联系,实体之间的协作,联合查询等也都依赖于这些实体关系。
  实体与数据库的映射可以通过DataAnnotation与FluentAPI两种方式来进行映射:
DataAnnotation
  DataAnnotation 特性由.NET 3.5中引进,给.NET中的类提供了一种添加验证的方式。DataAnnotation由命名空间ponentModel.DataAnnotations提供。下面列举实体模型中常用的DataAnnotation特性:
KeyAttribute:对应数据库中的主键
RequiredAttribute:对应数据库中字段的数据是否可以为null
MaxLengthAttribute:对应数据库中字符串类型字段的最大长度
MinLengthAttribute:在数据库中无对应,但在代码中字符串最小长度
ConcurrencyCheckAttribute:指定用于开放式并发检查的列的数据类型
TimestampAttribute:将列的数据类型指定为行版本
  ponentModel.DataAnnotations命名空间中只定义了部分实体验证的特性,在EntityFramework程序集中定义了更多的数据映射特性:
DatabaseGeneratedAttribute:标记指定实体属性是由数据库生成的,并指定生成策略(None数据库不生成值,Identity当插入行时,数据库生成值,Computed当插入或更新行时,数据库生成值)
ColumnAttribute:指定实体属性在数据库中的列名及数据类型
TableAttribute:指定实体类对应的数据表名
ForeignKeyAttribute:指定导航属性的外键字段
NotMappedAttribute:标记指定实体属性在创建数据库中不创建对应字段
ComplexTypeAttribute:标记指定实体属性是将一个对象作为另一个对象的属性,映射到数据库中则子对象表现为多个属性字段
  对于实体关系对应的数据表关系,无非&0:1,1:1,0:N,1:N,N:N&这几种,可以使用导航属性中的数据类型来表示,0&1端使用单实体类型表示,N端使用ICollection&T&集合类型表示。对于单实体端,默认是可为空的,即为0关系,如果要设置为1关系,要使用[Required]标签来进行标记。但对于一对一中的关系主体与依赖对象确无法做更细节的控制。&
Fluent API
  使用DataAnnotation非常简单,但对于EntityFramework中的特性,就要在实体类中引入EntityFramework程序集,但实体类最好能是保持与架构无关性的POCO类,才能更具通用性。所以,最好是在数据层中使用FluentAPI在数据层中进行实体类与数据库之间的映射工作。
  当然,ponentModel.DataAnnotations命名空间的DataAnnotation在EntityFramework程序集中也有相应的API:
HasKey - KeyAttribute:配置此实体类型的主键属性
IsRequired - RequiredAttribute:将此属性配置为必需属性。用于存储此属性的数据库列将不可以为null
HasMaxLength -&MaxLengthAttribute:将属性配置为具有指定的最大长度
IsConcurrencyToken -&ConcurrencyCheckAttribute:将属性配置为用作开放式并发标记
IsRowVersion -&TimestampAttribute:将属性配置为数据库中的行版本。实际数据类型将因使用的数据库提供程序而异。将属性设置为行版本会自动将属性配置为开放式并发标记。
  上面这些API均无需引用EntityFramework,推荐使用DataAnnotation方式来设置映射。
  以下API的DataAnnotation特性是在EntityFramework中定义,如果也使用DataAnnotation方式来设置映射,就会给实体类增加额外的第三方程序集的依赖。所以以下API的映射推荐使用FluentAPI的方式来设置映射:
ToTable - TableAttribute:配置此实体类型映射到的表名
HasColumnName -&ColumnAttribute:配置用于存储属性的数据库列的名称
HasForeignKey -&ForeignKeyAttribute:将关系配置为使用在对象模型中的外键属性。如果未在对象模型中公开外键属性,则使用Map方法
Ignore -&NotMappedAttribute:从模型中排队某个属性,使该属性不会映射到数据库
HasRequired:通过此实体类型配置必需关系。除非指定此关系,否则实体类型的实例将无法保存到数据库。数据库中的外键不可为null。
HasOptional:从此实体类型配置可选关系。实体类型的实例将能保存到数据库,而无需指定此关系。数据库中的外键可为null。
HasMany:从此实体类型配置一对多关系。
WithOptional:将关系配置为required:optional。(required:0&1端的1,表示必需,不可为null;optional:0&1端的0,表示可选,可为null。下同)
WithOptionalDependent:将关系配置为optional:optional。要配置的实体类型将成为依赖对象,且包含主体的外键。作为关系目标的实体类型将成为关系中的主体。
WithOptionalPrincipal:将关系配置为optional:optional。要配置的实体类型将成为关系中的主体。作为关系目标的实体类型将成为依赖对象,且包含主体的外键。
WithRequired:将关系的指定端配置为必需的,且在关系的另一端有导航属性。
WithRequiredDependent:将关系配置为required:required。要配置的实体类型将成为依赖对象,且包含主体的外键。作为关系目标的实体类型将成为关系中的主体。
WithRequiredPrincipal:将关系配置为required:required。要配置的实体类型将成为关系中的实体。作为关系目标的实体类型将成为依赖对象,且包含主体的外键。
WillCascadeOnDelete:配置是否对关系启用级联删除。
Map:将关系配置为使用未在对象模型中公开的外键属性。可通过指定配置操作来自定义列和表。如果指定了空的配置操作,则约定将生成列名。如果在对象模型中公开了外键属性,则使用 HasForeignKey 方法。并非所有关系都支持在对象模型中公开外键属性。
MapKey:配置外键的列名。
ToTable:配置外键列所在表的名称和架构。
  经常用到的DataAnnotation与FluentAPI列举完了,使用上还是遵守这个原则:
  如果在ponentModel.DataAnnotations命名空间存在相应的标签,就使用 DataAnnotation 的方式,如果不存在,则使用 FluentAPI 的方式。
映射代码示例
实体类关系图:
  上图是一个以用户信息为中心的实体关系图,关系说明如下:
一个用户可拥有一个可选的用户扩展信息(1 - 0)
一个用户扩展信息拥有一个必需的所属用户信息(0 - 1)
一个用户扩展信息拥有一个用户地址信息(复合类型)
一个用户可对应多个登录日志信息(1 - N)
一个登录日志拥有一个必需的所属用户信息(N- 1)
一个用户可以拥有多个角色(N - N)
一个角色可以分配给多个用户(N - N)
实体类定义:
1 namespace GMF.Demo.Core.Models
/// &summary&
实体类&&用户信息
/// &/summary&
[Description("用户信息")]
public class Member : Entity
public int Id { get; set; }
[Required]
[StringLength(20)]
public string UserName { get; set; }
[Required]
[StringLength(32)]
public string Password { get; set; }
[Required]
[StringLength(20)]
public string NickName { get; set; }
[Required]
[StringLength(50)]
public string Email { get; set; }
/// &summary&
/// 获取或设置 用户扩展信息
/// &/summary&
public virtual MemberExtend Extend { get; set; }
/// &summary&
/// 获取或设置 用户拥有的角色信息集合
/// &/summary&
public virtual ICollection&Role& Roles { get; set; }
/// &summary&
/// 获取或设置 用户登录记录集合
/// &/summary&
public virtual ICollection&LoginLog& LoginLogs { get; set; }
1 namespace GMF.Demo.Core.Models
/// &summary&
实体类&&用户扩展信息
/// &/summary&
[Description("用户扩展信息")]
public class MemberExtend : Entity
/// &summary&
/// 初始化一个 用户扩展实体类 的新实例
/// &/summary&
public MemberExtend()
Id = CombHelper.NewComb();
public Guid Id { get; set; }
public string Tel { get; set; }
public MemberAddress Address { get; set; }
public virtual Member Member { get; set; }
用户扩展信息
1 namespace GMF.Demo.Core.Models
/// &summary&
/// 用户地址信息
/// &/summary&
public class MemberAddress
[StringLength(10)]
public string Province { get; set; }
[StringLength(20)]
public string City { get; set; }
[StringLength(20)]
public string County { get; set; }
[StringLength(60, MinimumLength = 5)]
public string Street { get; set; }
用户地址信息
1 namespace GMF.Demo.Core.Models
/// &summary&
/// 实体类&&登录记录信息
/// &/summary&
[Description("登录记录信息")]
public class LoginLog : Entity
/// &summary&
/// 初始化一个 登录记录实体类 的新实例
/// &/summary&
public LoginLog()
Id = CombHelper.NewComb();
public Guid Id { get; set; }
[Required]
[StringLength(15)]
public string IpAddress { get; set; }
/// &summary&
/// 获取或设置 所属用户信息
/// &/summary&
public virtual Member Member { get; set; }
登录记录信息
1 namespace GMF.Demo.Core.Models
/// &summary&
实体类&&角色信息
/// &/summary&
[Description("角色信息")]
public class Role : Entity
public Role()
Id = CombHelper.NewComb();
public Guid Id { get; set; }
[Required]
[StringLength(20)]
public string Name { get; set; }
[StringLength(100)]
public string Description { get; set; }
/// &summary&
/// 获取或设置 角色类型
/// &/summary&
public RoleType RoleType { get; set; }
/// &summary&
/// 获取或设置 角色类型的数值表示,用于数据库存储
/// &/summary&
public int RoleTypeNum { get; set; }
/// &summary&
获取或设置 拥有此角色的用户信息集合
/// &/summary&
public virtual ICollection&Member& Members { get; set; }
1 namespace GMF.Demo.Core.Models
/// &summary&
/// 表示角色类型的枚举
/// &/summary&
[Description("角色类型")]
public enum RoleType
/// &summary&
/// 用户类型
/// &/summary&
[Description("用户角色")]
/// &summary&
/// 管理员类型
/// &/summary&
[Description("管理角色")]
角色类型(枚举)
实体类映射:
  实体类映射中,关系的映射配置在关系的两端都可以配置。例如,用户信息与登录信息的 一对多 关系可以在用户信息端配置:
    HasMany(m =& m.LoginLogs).WithRequired(n =& n.Member);
  等效于在登录日志信息端配置:
    HasRequired(m =& m.Member).WithMany(n =& n.LoginLogs);
  但是,如果所有的关系映射都在作为主体的用户信息端进行配置,势必造成用户信息端配置的臃肿与职责不明。所以,为了保持各个实体类型的职责单一,实体关系推荐在关系的非主体端进行映射。
  用户信息映射类,用户信息是关系的主体,所有的关系都不在此映射类中进行配置
1 namespace GMF.Demo.Core.Data.Configurations
public class MemberConfiguration : EntityTypeConfiguration&Member&
  用户扩展信息映射类,配置用户扩展信息与用户信息的 0:1 关系
1 namespace GMF.Demo.Core.Data.Configurations
public class MemberExtendConfiguration : EntityTypeConfiguration&MemberExtend&
public MemberExtendConfiguration()
HasRequired(m =& m.Member).WithOptional(n =& n.Extend);
  用户地址信息映射类,配置用户地址信息的复杂类型映射,复杂类型继承于 ComplexTypeConfiguration&&
1 namespace GMF.Demo.Core.Data.Configurations
public class MemberAddressConfiguration : ComplexTypeConfiguration&MemberAddress&
public MemberAddressConfiguration()
Property(m =& m.Province).HasColumnName("Province");
Property(m =& m.City).HasColumnName("City");
Property(m =& m.County).HasColumnName("County");
Property(m =& m.Street).HasColumnName("Street");
  登录记录信息映射,配置登录信息与用户信息的 N:1 的关系
1 namespace GMF.Demo.Core.Data.Configurations
public class LoginLogConfiguration : EntityTypeConfiguration&LoginLog&
public LoginLogConfiguration()
HasRequired(m =& m.Member).WithMany(n =& n.LoginLogs);
  角色信息映射,配置角色信息与用户信息的 N:N 的关系
1 namespace GMF.Demo.Core.Data.Configurations
public class RoleConfiguration : EntityTypeConfiguration&Role&
public RoleConfiguration()
HasMany(m =& m.Members).WithMany(n =& n.Roles);
映射类的应用:
  映射类需要在数据访问上下文中进行应用才能生效,只要在DbContext的OnModelCreating方法中进行映射配置添加即可。
1 protected override void OnModelCreating(DbModelBuilder modelBuilder)
//移除一对多的级联删除约定,想要级联删除可以在 EntityTypeConfiguration&TEntity&的实现类中进行控制
modelBuilder.Conventions.Remove&OneToManyCascadeDeleteConvention&();
//多对多启用级联删除约定,不想级联删除可以在删除前判断关联的数据进行拦截
//modelBuilder.Conventions.Remove&ManyToManyCascadeDeleteConvention&();
modelBuilder.Configurations.Add(new MemberConfiguration());
modelBuilder.Configurations.Add(new MemberExtendConfiguration());
modelBuilder.Configurations.Add(new MemberAddressConfiguration());
modelBuilder.Configurations.Add(new RoleConfiguration());
modelBuilder.Configurations.Add(new LoginLogConfiguration());
&  经过上面的折腾,数据库结构已经大变,项目当然运行不起来了。
  根据提示,必须进行迁移来更新数据库结构。EntityFramework的数据迁移通过& 来进行。打开程序包管理器控制台(Package Manager Console),键入&&get-help EntityFramework&命令,可以获得相关的帮助信息。
  若想了解各个子命令的帮助细节,也可键入&get-help 子命令&命令,例如:get-help Enable-Migrations
  下面我们来对项目进行数据迁移,在我们的项目中,EntityFramework的依赖止于项目GMF.Demo.Core.Data,项目的数据迁移也是在此项目中进行。迁移步骤如下:
在&程序包管理器控制台&键入命令:Enable-Migrations -ProjectName GMF.Demo.Core.Data
添加后,项目中添加了一个名为Migrations的文件夹添加生成以下代码:
1 namespace GMF.Demo.Core.Data.Migrations
internal sealed class Configuration : DbMigrationsConfiguration&DemoDbContext&
public Configuration()
AutomaticMigrationsEnabled = false;
protected override void Seed(DemoDbContext context)
This method will be called after migrating to the latest version.
You can use the DbSet&T&.AddOrUpdate() helper extension method
to avoid creating duplicate seed data. E.g.
context.People.AddOrUpdate(
p =& p.FullName,
new Person { FullName = "Andrew Peters" },
new Person { FullName = "Brice Lambson" },
new Person { FullName = "Rowan Miller" }
方法Seed中可以进行数据迁移后的数据初始化工作,将在每次迁移之后运行。如上代码所示,AddOrUpdate是IDbSet&TEntity&的扩展方法,如果指定条件的数据不存在,则会添加,如果存在,会更新。所以,如果数据是通过此方法来初始化的,在与业务更新之后,再次进行数据迁移后,还是会被还原。还有一个名为InitialCreate的类,配置生成数据库的细节:
1 namespace GMF.Demo.Core.Data.Migrations
public partial class InitialCreate : DbMigration
public override void Up()
CreateTable(
"dbo.Roles",
Id = c.Guid(nullable: false),
Name = c.String(nullable: false, maxLength: 20),
Description = c.String(maxLength: 100),
IsDeleted = c.Boolean(nullable: false),
AddDate = c.DateTime(nullable: false),
Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
.PrimaryKey(t =& t.Id);
CreateTable(
"dbo.Members",
Id = c.Int(nullable: false, identity: true),
UserName = c.String(nullable: false, maxLength: 20),
Password = c.String(nullable: false, maxLength: 32),
NickName = c.String(nullable: false, maxLength: 20),
Email = c.String(nullable: false, maxLength: 50),
IsDeleted = c.Boolean(nullable: false),
AddDate = c.DateTime(nullable: false),
Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
.PrimaryKey(t =& t.Id);
CreateTable(
"dbo.MemberExtends",
Id = c.Guid(nullable: false),
IsDeleted = c.Boolean(nullable: false),
AddDate = c.DateTime(nullable: false),
Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
Member_Id = c.Int(nullable: false),
.PrimaryKey(t =& t.Id)
.ForeignKey("dbo.Members", t =& t.Member_Id)
.Index(t =& t.Member_Id);
CreateTable(
"dbo.LoginLogs",
Id = c.Guid(nullable: false),
IpAddress = c.String(nullable: false, maxLength: 15),
IsDeleted = c.Boolean(nullable: false),
AddDate = c.DateTime(nullable: false),
Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
Member_Id = c.Int(),
.PrimaryKey(t =& t.Id)
.ForeignKey("dbo.Members", t =& t.Member_Id)
.Index(t =& t.Member_Id);
CreateTable(
"dbo.MemberRoles",
Member_Id = c.Int(nullable: false),
Role_Id = c.Guid(nullable: false),
.PrimaryKey(t =& new { t.Member_Id, t.Role_Id })
.ForeignKey("dbo.Members", t =& t.Member_Id, cascadeDelete: true)
.ForeignKey("dbo.Roles", t =& t.Role_Id, cascadeDelete: true)
.Index(t =& t.Member_Id)
.Index(t =& t.Role_Id);
public override void Down()
DropIndex("dbo.MemberRoles", new[] { "Role_Id" });
DropIndex("dbo.MemberRoles", new[] { "Member_Id" });
DropIndex("dbo.LoginLogs", new[] { "Member_Id" });
DropIndex("dbo.MemberExtends", new[] { "Member_Id" });
DropForeignKey("dbo.MemberRoles", "Role_Id", "dbo.Roles");
DropForeignKey("dbo.MemberRoles", "Member_Id", "dbo.Members");
DropForeignKey("dbo.LoginLogs", "Member_Id", "dbo.Members");
DropForeignKey("dbo.MemberExtends", "Member_Id", "dbo.Members");
DropTable("dbo.MemberRoles");
DropTable("dbo.LoginLogs");
DropTable("dbo.MemberExtends");
DropTable("dbo.Members");
DropTable("dbo.Roles");
InitialCreate
执行&Add-Migration FirstMigration&命令,添加一个名为FirstMigration的迁移
执行&Update-Database&命令,更新数据库架构如果更新数据库存在冲突而不能执行更新,可以添加 -Force强制执行,例如:&Update-Database -Force&
设置自动迁移每次都通过控制台来进行迁移太过麻烦,可以设置为自动迁移。有以下两个参数可以对自动迁移进行设置:  1.&AutomaticMigrationsEnabled:获取或设置 指示迁移数据库时是否可使用自动迁移的值。  2.&AutomaticMigrationDataLossAllowed:获取或设置 指示是否可接受自动迁移期间的数据丢失的值。如果设置为false,则将在数据丢失可能作为自动迁移一部分出现时引发异常。修改迁移的Configuration类如下:
1 namespace GMF.Demo.Core.Data.Migrations
internal sealed class Configuration : DbMigrationsConfiguration&DemoDbContext&
public Configuration()
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
protected override void Seed(DemoDbContext context)
List&Member& members = new List&Member&
new Member { UserName = "admin", Password = "123456", Email = "", NickName = "管理员" },
new Member { UserName = "gmfcn", Password = "123456", Email = "mf.", NickName = "郭明锋" }
DbSet&Member& memberSet = context.Set&Member&();
memberSet.AddOrUpdate(m =& new { m.Id }, members.ToArray());
&修改数据库初始化策略如下:
Database.SetInitializer(new MigrateDatabaseToLatestVersion&DemoDbContext, Configuration&());
&  经过上面的演练,我们的项目变成如下图所示:
  现在的项目中,数据访问上下文DemoDbContext代码如下所示:
1 namespace GMF.Demo.Core.Data.Context
/// &summary&
Demo项目数据访问上下文
/// &/summary&
[Export(typeof(DbContext))]
public class DemoDbContext : DbContext
#region 构造函数
/// &summary&
初始化一个 使用连接名称为&default&的数据访问上下文类 的新实例
/// &/summary&
public DemoDbContext()
: base("default") { }
/// &summary&
/// 初始化一个 使用指定数据连接名称或连接串 的数据访问上下文类 的新实例
/// &/summary&
public DemoDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) { }
#endregion
#region 属性
public DbSet&Role& Roles { get; set; }
public DbSet&Member& Members { get; set; }
public DbSet&MemberExtend& MemberExtends { get; set; }
public DbSet&LoginLog& LoginLogs { get; set; }
#endregion
protected override void OnModelCreating(DbModelBuilder modelBuilder)
//移除一对多的级联删除约定,想要级联删除可以在 EntityTypeConfiguration&TEntity&的实现类中进行控制
modelBuilder.Conventions.Remove&OneToManyCascadeDeleteConvention&();
//多对多启用级联删除约定,不想级联删除可以在删除前判断关联的数据进行拦截
//modelBuilder.Conventions.Remove&ManyToManyCascadeDeleteConvention&();
modelBuilder.Configurations.Add(new MemberConfiguration());
modelBuilder.Configurations.Add(new MemberExtendConfiguration());
modelBuilder.Configurations.Add(new MemberAddressConfiguration());
modelBuilder.Configurations.Add(new RoleConfiguration());
modelBuilder.Configurations.Add(new LoginLogConfiguration());
&  由代码可以看出,当前的上下文类与业务实体是强耦合的,分别耦合在DbSet&TEntity&的属性与OnModelCreating方法上。如果要解耦,对于属性,可以使用DbContext.Set&TEntity&()方法来实现指定实体的属性,对于OnModelCreating中的方法实现中的映射配置对象,则可提取一个通用接口,通过接口进行分别映射。
  定义接口如下:
1 namespace ponent.Data
/// &summary&
实体映射接口
/// &/summary&
[InheritedExport]
public interface IEntityMapper
/// &summary&
将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// &/summary&
/// &param name="configurations"&实体映射配置注册器&/param&
void RegistTo(ConfigurationRegistrar configurations);
  IEntityMapper接口添加了MEF的InheritedExport特性,该特性可以沿着继承链传递所施加的特性。在需要的时候,就可以通过ImportManyAttribute一次性导出所有实现了IEntityMapper接口的实现类对象。
&  在实体映射类中添加IEntityMapper的实现,如角色映射类中:
1 namespace GMF.Demo.Core.Data.Configurations
public class RoleConfiguration : EntityTypeConfiguration&Role&, IEntityMapper
public RoleConfiguration()
HasMany(m =& m.Members).WithMany(n =& n.Roles);
public void RegistTo(ConfigurationRegistrar configurations)
configurations.Add(this);
  下面来对数据访问上下文进行改造,并转移到数据组件 ponent.Data 中。
  添加一个IEnumerable&IEntityMapper&类型的属性EntityMappers,并添加ImportManyAttribute,用于引入所有实现了IEntityMapper的类的对象。
  在重写的OnModelCreating方法中,遍历EntityMappers集合,调用其中的RegistTo进行实体映射类对象的添加。
1 namespace ponent.Data
/// &summary&
EF数据访问上下文
/// &/summary&
[Export(typeof (DbContext))]
public class EFDbContext : DbContext
public EFDbContext()
: base("default") { }
public EFDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) { }
[ImportMany]
public IEnumerable&IEntityMapper& EntityMappers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Conventions.Remove&OneToManyCascadeDeleteConvention&();
if (Configuration == null)
foreach (var mapper in EntityMappers)
mapper.RegistTo(modelBuilder.Configurations);
&  上下文EFDbContext在单元操作类 EFRepositoryContext 类中进行使用:
1 namespace ponent.Data
/// &summary&
数据单元操作类
/// &/summary&
[Export(typeof (IUnitOfWork))]
internal class EFRepositoryContext : UnitOfWorkContextBase
/// &summary&
获取 当前使用的数据访问上下文对象
/// &/summary&
protected override DbContext Context
get { return EFDbC }
[Import(typeof (DbContext))]
private EFDbContext EFDbContext { get; set; }
&  经过如此重构,DbContext上下文就与实体无关了,数据访问功能与业务实体便完成解耦。
  我们来对比重构前后的变化:
  我们来对比一下重构前后的变化:
【缺】上下文与实体类强耦合,不通用,每添加一个实体都要修改上下文类
【优】上下文中有实体信息,支持手动数据迁移,可以使用命令行进行迁移功能的启用、添加迁移脚本
【优】上下文与实体类解耦,能通用
【缺?优?】上下文中没有实体信息,无法支持手动迁移(手动迁移需要上下文中的实体信息来生成迁移脚本),但支持自动迁移(使用运行时的上下文,这时已经加载了实体信息了)
  那么,是否重构,就看具体需求而定了。不过项目最终还是要使用自动迁移的,因为在项目上线之后,如果需要迁移线上的数据库,那时数据库在服务器中了,就不可能使用VS的控制台命令行的方式来添加迁移了,只能启用自动迁移来完成数据库结构的变更迁移。
  还有个问题,就是没有了带实体的上下文类,无法通过命令行的方式来启用数据迁移功能了,怎样启用数据迁移呢?这时需要手动在GMF.Demo.Core.Data项目中手动来添加一个Migrations文件夹,再在文件夹中添加一个继承自&DbMigrationsConfiguration&TDbContext& 的Configuration类来启用数据迁移功能,并且设置AutomaticMigrationsEnabled与AutomaticMigrationDataLossAllowed两个属性为true来启用自动迁移。最后,还需要将数据库初始化策略更改为&迁移数据库到最新版本(MigrateDatabaseToLatestVersion)&,即&Database.SetInitializer(new MigrateDatabaseToLatestVersion&TDbContext, Configuration&()); 来引用Configuration类。
  为了让大家能第一时间获取到本架构的最新代码,也为了方便我对代码的管理,本系列的源码已加入微软的开源项目网站 ,地址为:
  可以通过下列途径获取到最新代码:
如果你是本项目的参与者,可以通过VS自带的团队TFS直接连接到&:443/tfs/TFS17 获取最新代码
如果你安装有SVN客户端(亲测可用),可以连接到&https://gmframework./svn 获取最新代码
如果以上条件都不满足,你可以进入页面&/SourceControl/latest 查看最新代码,也可以点击页面上的&Download 链接进行压缩包的下载,你还可以点击页面上的 History 链接获取到历史版本的源代码
如果你想和大家一起学习MVC,学习EF,欢迎加入Q群:5008599(群发言仅限技术讨论,拒绝闲聊,拒绝酱油,拒绝广告)
如果你想与我共同来完成这个开源项目,可以随时联系我。
MVC实用架构设计(〇)&&
MVC实用架构设计(一)&&
MVC实用架构设计(二)&&
MVC实用架构设计(三)&&
MVC实用架构设计(三)&&EF-Code First(2):实体映射、数据迁移,重构
MVC实用架构设计(三)&&
MVC实用架构设计(三)&&
MVC实用架构设计(三)&&
MVC实体架构设计(三)&&
未完待续。。。
阅读(...) 评论()

我要回帖

更多关于 springmvc中的mapper 的文章

 

随机推荐