@Valid和@validated用法与自定义验证注解


原博客,点击这里
很全的一篇文章,点击这里文章2,点击这里

自己的理解

一般只有save(保存),update(修改)方法才会。使用参数校验。

注解:

参考文章,点击这里
在这里插入图片描述

1.@Valid:

@Valid注解用于校验,所属包为:javax.validation.Valid。

① 首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@Min,如下代码(age属于Girl类中的属性):

@Min(value = 18,message = "未成年禁止入内")  
private Integer age;  

② 其次在controller层的方法的要校验的参数上添加@Valid注解,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息,如下代码:

@PostMapping("/girls")  
public Girl addGirl(@Valid Girl girl, BindingResult bindingResult) {  
    if(bindingResult.hasErrors()){  
        System.out.println(bindingResult.getFieldError().getDefaultMessage());  
        return null;  
    }  
    return girlResposity.save(girl);  
}  

bindingResult.getFieldError.getDefaultMessage()用于获取相应字段上添加的message中的内容,如:@Min注解中message属性的内容

2.@Validated:

@Valid是javax.validation里的。

@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Valid不提供分组功能
@Validated的特殊用法

1、分组

当一个实体类需要多种验证方式时,例:对于一个实体类的id来说,新增的时候是不需要的,对于更新时是必须的。

可以通过groups对验证进行分组

分组接口类(通过向groups分配不同类的class对象,达到分组目的):

package com.valid.interfaces;  
  
public interface First {  
  
}  

实体类:

package com.valid.pojo;  
  
import javax.validation.constraints.Size;  
import org.hibernate.validator.constraints.NotEmpty;  
  
import com.valid.interfaces.First;  
  
public class People {  
      
    //在First分组时,判断不能为空  
    @NotEmpty(groups={First.class})  
    private String id;  
      
    //name字段不为空,且长度在3-8之间  
    @NotEmpty  
    @Size(min=3,max=8)  
    private String name;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
}  

注:

(1)不分配groups,默认每次都要进行验证

(2)对一个参数需要多种验证方式时,也可通过分配不同的组达到目的。例:
控制类:

package com.valid.controller;  
  
import org.springframework.stereotype.Controller;  
import org.springframework.validation.BindingResult;  
import org.springframework.validation.annotation.Validated;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.ResponseBody;  
  
import com.valid.interfaces.First;  
import com.valid.pojo.People;  
  
@Controller  
public class FirstController {  
      
    @RequestMapping("/addPeople")  
    //不需验证ID  
    public @ResponseBody String addPeople(@Validated People p,BindingResult result)  
    {  
        System.out.println("people's ID:" + p.getId());  
        if(result.hasErrors())  
        {  
            return "0";  
        }  
        return "1";  
    }  
      
    @RequestMapping("/updatePeople")  
    //需要验证ID  
    public @ResponseBody String updatePeople(@Validated({First.class}) People p,BindingResult result)  
    {  
        System.out.println("people's ID:" + p.getId());  
        if(result.hasErrors())  
        {  
            return "0";  
        }  
        return "1";  
    }  
}  

注:
@Validated没有添加groups属性时,默认验证没有分组的验证属性,如该例子:People的name属性。

@Validated没有添加groups属性时,所有参数的验证类型都有分组(即本例中People的name的@NotEmpty、@Size都添加groups属性),则不验证任何参数

2、组序列

顺序相关的参考文章,点击这里
默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要。

例:

(1)第二个组中的约束验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。

(2)某个组的验证比较耗时,CPU 和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。
一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。

分组接口类 (通过@GroupSequence注解对组进行排序):

package com.valid.interfaces;  
  
public interface First {  
  
}  
package com.valid.interfaces;  
  
public interface Second {  
  
}  
package com.valid.interfaces;  
  
import javax.validation.GroupSequence;  
  
@GroupSequence({First.class,Second.class})  
public interface Group {  
  
}  

实体类:

package com.valid.pojo;  
  
import javax.validation.constraints.Size;  
import org.hibernate.validator.constraints.NotEmpty;  
  
import com.valid.interfaces.First;  
import com.valid.interfaces.Second;  
  
public class People {  
      
    //在First分组时,判断不能为空  
    @NotEmpty(groups={First.class})  
    private String id;  
      
    //name字段不为空,且长度在3-8之间  
    @NotEmpty(groups={First.class})  
    @Size(min=3,max=8,groups={Second.class})  
    private String name;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
}  

控制类:

package com.valid.controller;  
  
import org.springframework.stereotype.Controller;  
import org.springframework.validation.BindingResult;  
import org.springframework.validation.annotation.Validated;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.ResponseBody;  
  
import com.valid.interfaces.Group;  
import com.valid.pojo.People;  
import com.valid.pojo.Person;  
  
@Controller  
public class FirstController {  
      
    @RequestMapping("/addPeople")  
    //不需验证ID  
    public @ResponseBody String addPeople(@Validated({Group.class}) People p,BindingResult result)  
    {  
        if(result.hasErrors())  
        {  
            return "0";  
        }  
        return "1";  
    }  
} 

3、验证多个对象

一个功能方法上处理多个模型对象时,需添加多个验证结果对象

package com.valid.controller;  
  
import org.springframework.stereotype.Controller;  
import org.springframework.validation.BindingResult;  
import org.springframework.validation.annotation.Validated;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.ResponseBody;  
  
import com.valid.pojo.People;  
import com.valid.pojo.Person;  
  
@Controller  
public class FirstController {  
      
    @RequestMapping("/addPeople")  
    public @ResponseBody String addPeople(@Validated People p,BindingResult result,@Validated Person p2,BindingResult result2)  
    {  
        if(result.hasErrors())  
        {  
            return "0";  
        }  
        if(result2.hasErrors())  
        {  
            return "-1";  
        }  
        return "1";  
    }  
}  
<think>嗯,用户现在想在C#的DataGrid中实现点击表头自动排序,并且要求不区分大小写。之前我已经给出了两种方法,一种是利用WPF DataGrid的原生排序支持,另一种是通过数据绑定集合自动排序。现在用户提到要确保排序不区分大小写,所以需要调整这些方法。 首先,我需要回忆一下在WPF中排序是如何工作的。DataGrid的默认排序可能依赖于数据源本身的排序方式,比如如果数据源是字符串,默认可能会区分大小写。用户的需求是让排序时不考虑大小写,所以需要自定义排序逻辑。 查看用户提供的引用,引用[1]提到了删除数据库记录并重新绑定数据到DataGrid,这可能当前问题无关。引用[2]讨论了WPF DataGrid的自定义表头样式,这可能涉及到列的样式设置,但用户现在需要的是排序逻辑的调整。引用[3]提到DataGridView点击表头无法自动排序的设置,但用户现在使用的是WPF的DataGrid,而不是Windows Forms的DataGridView,所以可能需要忽略这个引用。 接下来,我应该考虑如何在不区分大小写的情况下进行排序。在WPF中,当使用CollectionViewSource进行排序时,可以通过自定义比较器来实现。例如,在SortDescription中使用自定义的排序逻辑,或者在处理Sorting事件时修改排序方式。 在之前的回答中,用户已经使用了Sorting事件ListCollectionView。因此,可能的解决方案是:在处理Sorting事件时,自定义排序逻辑,将字符串转换为统一的大小写(比如全小写或全大写)后再进行比较。 另外,可能需要考虑使用自定义的排序规则,比如实现IComparer接口,并在SortDescription中指定该比较器。但SortDescription本身是否支持自定义比较器呢?可能需要进一步查阅文档。 或者,在数据绑定的时候,将实际显示的属性排序用的属性分开。例如,在数据模型中添加一个用于排序的属性,该属性是原属性的全小写版本,然后让DataGrid根据这个属性进行排序,但显示原属性。这种方法可能需要在数据层进行处理,而不是在UI层。 不过,用户可能希望直接在UI层解决这个问题,而不修改数据模型。因此,自定义比较器可能更合适。 在WPF中,ListCollectionView的排序默认使用的是数据项属性的默认比较方式,对于字符串,这通常是区分大小写的。因此,需要覆盖这一行为,可能在Sorting事件中,取消默认的排序,然后应用自定义的排序逻辑,使用不区分大小写的比较。 例如,在Sorting事件处理程序中,获取要排序的属性方向,然后对数据源进行重新排序,使用不区分大小写的比较器。这可能涉及到将数据转换为列表,然后使用LINQ的OrderBy或OrderByDescending,并指定StringComparer.OrdinalIgnoreCase。 但是,直接修改数据源的顺序可能会影响绑定,因此更好的方法可能是通过CollectionViewSource的CustomSort属性来指定自定义的IComparer实现。这样,排序逻辑会被CollectionView处理,而无需修改数据源本身。 例如,创建一个实现IComparer接口的类,在Compare方法中进行不区分大小写的比较。然后在Sorting事件中设置CustomSort为该比较器的实例。 不过,需要确认CustomSort是否会影响所有排序,还是可以按列指定。可能需要根据不同的列动态调整比较器,但这样可能比较复杂。 另一种方法是在数据源的属性中返回已处理的大小写不敏感的值。例如,在绑定到DataGrid的属性时,返回全小写的字符串,但显示时仍然保留原始大小写。这可能需要使用转换器或者在数据模型中添加额外的属性。 不过,用户可能希望保持数据模型的纯净,因此更倾向于在UI层处理。 总结可能的步骤: 1. 处理DataGrid的Sorting事件,取消默认的排序处理。 2. 获取当前点击的列排序方向。 3. 使用自定义的比较逻辑(不区分大小写)对数据源进行排序。 4. 更新CollectionView的SortDescriptions,并刷新视图。 或者,使用CustomSort属性指定全局的自定义比较器,但需要注意这会影响所有列的排序,可能需要更复杂的逻辑来处理不同列的数据类型。 现在需要具体实现这些步骤。例如,在Sorting事件处理程序中,使用LINQ对数据进行排序,然后重新绑定到DataGrid。但这样可能效率不高,特别是在大数据量的情况下。 更好的方法可能是通过修改CollectionView的SortDescriptions,并指定自定义的排序逻辑。但SortDescription本身不支持直接指定比较器,所以可能需要使用自定义的排序方式。 查阅文档发现,ListCollectionView有一个CustomSort属性,可以设置为IComparer的实例。因此,可以创建一个自定义的IComparer,根据当前的排序属性方向进行比较,不区分大小写。 例如: public class CaseInsensitiveComparer : IComparer { private string _sortProperty; private ListSortDirection _direction; public CaseInsensitiveComparer(string sortProperty, ListSortDirection direction) { _sortProperty = sortProperty; _direction = direction; } public int Compare(object x, object y) { var propertyX = x.GetType().GetProperty(_sortProperty).GetValue(x, null)?.ToString().ToLower(); var propertyY = y.GetType().GetProperty(_sortProperty).GetValue(y, null)?.ToString().ToLower(); int result = string.Compare(propertyX, propertyY, StringComparison.OrdinalIgnoreCase); return _direction == ListSortDirection.Ascending ? result : -result; } } 然后在Sorting事件处理中: private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e) { e.Handled = true; var column = e.Column; var sortProperty = column.SortMemberPath; var direction = column.SortDirection != ListSortDirection.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending; var view = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource) as ListCollectionView; if (view != null) { view.CustomSort = new CaseInsensitiveComparer(sortProperty, direction); } column.SortDirection = direction; } 但需要测试这种方法是否有效,以及是否支持多列排序。另外,CustomSort会覆盖所有现有的排序描述,所以可能需要在每次排序时重新创建比较器,包含所有当前活动的排序条件,这可能比较复杂。 另一种方法是,在SortDescription中添加基于不区分大小写的属性,但这需要数据源中的属性已经是处理过的大小写不敏感形式,或者通过转换器来实现。 或者,在数据绑定时,使用转换器将属性转换为小写,但显示原始值。例如,在绑定到Age列时,实际绑定到一个转换后的属性,但这可能需要修改数据模型或使用转换器。 不过,用户可能更倾向于在代码中处理,而不修改XAML或数据模型。因此,回到之前的CustomSort方法,可能在每次排序时,设置CustomSort为一个新的比较器,处理当前列的排序属性,并考虑方向。 这样,每次点击列时,会创建一个新的CaseInsensitiveComparer实例,根据当前列方向进行比较,从而实现不区分大小写的排序。 此外,还需要处理多列排序的情况,但用户可能只需要单列排序。如果需要多列排序,可能需要更复杂的逻辑来维护多个排序条件,但用户的问题可能仅限于单列排序。 综上,最终的解决方案可能是在Sorting事件处理中,设置ListCollectionView的CustomSort属性为自定义的IComparer,该比较器在比较时使用不区分大小写的字符串比较。</think>在C# DataGrid中实现不区分大小写的表头自动排序,可通过以下两种核心方案实现: ### 方案一:自定义排序比较器(推荐方法) ```csharp public class CaseInsensitiveComparer : IComparer { private readonly string _propertyName; private readonly ListSortDirection _direction; public CaseInsensitiveComparer(string propertyName, ListSortDirection direction) { _propertyName = propertyName; _direction = direction; } public int Compare(object x, object y) { var propX = x.GetType().GetProperty(_propertyName)?.GetValue(x)?.ToString().ToLower(); var propY = y.GetType().GetProperty(_propertyName)?.GetValue(y)?.ToString().ToLower(); int result = string.Compare(propX, propY, StringComparison.OrdinalIgnoreCase); return _direction == ListSortDirection.Ascending ? result : -result; } } // 在Sorting事件中应用比较器 private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e) { e.Handled = true; var column = e.Column; var direction = (column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending; if (CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource) is ListCollectionView view) { view.CustomSort = new CaseInsensitiveComparer(column.SortMemberPath, direction); } column.SortDirection = direction; } ``` ### 方案二:修改数据源属性(数据层处理) ```csharp // 数据模型增加排序专用属性 public class Person { public string Name { get; set; } public string NameForSort => Name?.ToLower(); // 排序专用属性 } // XAML绑定排序路径 <DataGridTextColumn Header="姓名" Binding="{Binding Name}" SortMemberPath="NameForSort"/> ``` ### 关键实现要点 1. **比较器原理**:通过实现$IComparer$接口,在字符串比较前执行$ToLower()$标准化处理[^2] 2. **事件拦截**:在$Sorting$事件中设置$e.Handled=true$阻止默认排序逻辑 3. **视图控制**:通过$ListCollectionView.CustomSort$属性注入自定义排序规则 4. **双向同步**:更新$column.SortDirection$保持表头排序状态指示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值