Skip to content

exitsoft orm 基于spring data jpa 持久化使用说明

exitsoft edited this page Jan 29, 2013 · 6 revisions

exitsoft orm 是对持久化层所使用到的框架进行封装,exitsof orm目前支持Hibernate4和spring data jpa,至于spring data jpa 是如何使用的可以参考:官方首页的文档的文档,或者参考IBM的文章:使用Spring Data JPA简化JPA开发,本文介绍exitsoft orm对spring data jpa的扩展,和在一起是怎么使用.

对于exitsoft orm对spring data jpa的扩展有:

  1. 添加了一个BasicJpaRepository<T,D> 接口该接口继承了JpaRepository<T,D>, JpaSpecificationExecutor<T,D>,目的是为了通过继承该接口.可以直接通过propertyFilter的查询
  2. 对BasicJpaRepository<T,D>做了实现类JpaSupportRepository<T,D>,该类是继承自SimpleJpaRepository的扩展类.
  3. 在JpaSupportRepository<T,D>类中添加了状态删除以及,转码功能.

BasicJpaRepository<T,D>

BasicJpaRepository<T,D>只要的扩展方法有.对PropertyFilter的查询和表达式的查询(具体表达式和PropertyFilter和Hibernate是一样使用的),可以参考:exitsoft orm 基于 Hibernate 4 持久化使用说明.的表达式章节.

要使用到BasicJpaRepository<T,D>需要在spring data jpa配置文件中对jpa:repositories的factory-class属性添加一个类:org.exitsoft.orm.core.spring.data.jpa.factory.BasicRepositoryFactoryBean:

<jpa:repositories base-package="你的repository包路径" 
							  transaction-manager-ref="transactionManager" 
							  factory-class="org.exitsoft.orm.core.spring.data.jpa.factory.BasicRepositoryFactoryBean"
							  entity-manager-factory-ref="entityManagerFactory"  />

因为BasicJpaRepository<T,D>的实现类是JpaSupportRepository<T,D>,所以要把spring data jpa默认使用的:SimpleJpaRepository<T,D>替换为:JpaSupportRepository<T,D>类,BasicRepositoryFactoryBean就是做了这个替换工作.

当配置好spring data jpa配置文件之后,BasicJpaRepository<T,D>就可以使用了.

现在有一个实体:

@Entity
@Table(name="TB_DATA_DICTIONARY")
public class DataDictionary extends UniversallyUniqueIdentifier{
	
	//名称
	private String name;
	//值
	private String value;
	//类型
	private String type = "S";
	//备注
	private String remark;
	//所属类别
	public DictionaryCategory category;
	//五笔编码
	private String wubiCode;
	//拼音编码
	private String pinYinCode;
	//状态,1:启用,2:禁用,3:删除
	private Integer state;

	//----------GETTER/SETTER-----------//
}

通过该实体的描述的数据访问为:

public interface DataDictionaryDao extends BasicJpaRepository<DataDictionary, String> {

}

对于CURD的基本操作都和原有的spring data jpa提供的方法一样使用,该方法的实现类都是来源与SimpleJpaRepository<T,D>,而BasicJpaRepository<T,D>主要扩展的是查询方法:

DataDictionary entity = new DataDictionary();
dd.setName("test");
dd.setValue("01")
//dd.set...
//保存
dataDictionaryDao.save(entity);
//删除
//dataDictionaryDao.delete(entity);

List<PropertyFilter> filters = Lists.newArrayList(
	PropertyFilters.build("EQI_id", "1234567"),
);
dictionaryCategoryDao.findAll(filters);
//sql:select from TB_DATA_DICTIONARY where id = ?;

List<PropertyFilter> filters = Lists.newArrayList(
	PropertyFilters.build("EQI_id", "1234567"),
);
dictionaryCategoryDao.findAll(filters);
//sql:select from TB_DATA_DICTIONARY where state <> ?;

List<PropertyFilter> filters = Lists.newArrayList(
	PropertyFilters.build("LIKES_name", "t"),
	PropertyFilters.build("NEI_state", "3")
);
//---------------------多条件查询---------------------//
dictionaryCategoryDao.findAll(filters);
//sql:select from TB_DATA_DICTIONARY where state <> ? and name like ?;

//---------------------分页查询---------------------//
Pageable pageable = new PageRequest(0, 10);
Page<DataDictionary> page = dictionaryCategoryDao.findAll(pageable,filters);

####状态删除@StateDelete注解

在大部分的业务系统中有某些表对于delete操作都有这么一个需求,就是不物理删除,只做状态删除。BasicJpaRepository<T,D>提供了这种功能。只要在实体类加上@StateDelete注解后调用delete方法,将会改变实体的某个字段做已删除的状态值。

@Entity
@Table(name="TB_DATA_DICTIONARY")
@StateDelete(propertyName = "state", value = "3")
public class DataDictionary extends UniversallyUniqueIdentifier{
	
	//名称
	private String name;
	//值
	private String value;
	//类型
	private String type = "S";
	//备注
	private String remark;
	//所属类别
	public DictionaryCategory category;
	//五笔编码
	private String wubiCode;
	//拼音编码
	private String pinYinCode;
	//状态,1:启用,2:禁用,3:删除
	private Integer state;

	//----------GETTER/SETTER-----------//
}

public interface DataDictionaryDao extends BasicJpaRepository<DataDictionary, String> {

}

dataDictionaryDao.delete(entity);
//sql:update TB_DATA_DICTIONARY set state = ? where id = ?

如果要用到这个功能,记得把org.exitsoft.orm.core.spring.data.jpa.factory.BasicRepositoryFactoryBean类配置到jpa:repositories标签的factory-class属性中

@StateDelete属性说明:

属性名称 属性类型 描述
propertyName java.lang.String 当delete时对实体的哪个属性值改变为删除后的状态值
value java.lang.String 当delete时要改变的值是什么,如:@StateDelete(propertyName = "state", value = "3")表示要改变的值为3
type org.exitsoft.orm.core.PropertyType 该属性表示propertyName的值类型是什么,有时候,状态值不一定是Integer类型,很有可能是其他类型,可以根据org.exitsoft.orm.core.PropertyType类指定值是什么

####字段转码@ConvertCode注解

在业务系统中,有时候会有这么一个业务需求,当保存某个对象的时候,将某个对象字段值转换成某个值,并赋值给某个属性在保存。比如一个字典类,当我保存时把name的值转换为五笔与拼音值后在保存该对象,这种需求可以解决用户在页面不管用户输入的是中文、英文、中文拼音、中文的五笔,都可以通过这种方式去查询出结果:

SELECT * FROM TB_DATA_DICTIONARY WHERE NAME LINKE '?%' OR WUBICODE LINKE '?%' OR PINYINCODE LINKE '?%'

那么现在有一个实体,当保存时候我要把name的值转换为五笔和拼音在赋值给pinyinCode wubiCode字段的话,可以写成这样:

@Entity
@Table(name="TB_DATA_DICTIONARY")
@ConvertCode(
	convertPropertys={
			@ConvertProperty(propertyNames={"wubiCode","pinYinCode"},
			strategyClass=PinYinWuBiConvertStrategy.class)
	},
	fromProperty="name",
	executeMehtod=ExecuteMehtod.Save
)
public class DataDictionary extends UniversallyUniqueIdentifier{
	
	//名称
	private String name;
	//值
	private String value;
	//类型
	private String type = "S";
	//备注
	private String remark;
	//所属类别
	public DictionaryCategory category;
	//五笔编码
	private String wubiCode;
	//拼音编码
	private String pinYinCode;
	//状态,1:启用,2:禁用,3:删除
	private Integer state;

	//----------GETTER/SETTER-----------//
}

/**
 * 拼音五笔转码策略
 * 
 * @author vincent
 *
 */
public class PinYinWuBiConvertStrategy implements CodeStrategy{
	
	
	public Object convertCode(Object value,String propertyName) {
		if (value == null) {
			return "";
		}
		if (propertyName.equals("wubiCode")) {
			return value.toString() + "的五笔";
		} else {
			return value.toString() + "的拼音";
		}
	}
	
}

public interface DataDictionaryDao extends BasicJpaRepository<DataDictionary, String> {

}

DataDictionary dd = new DataDictionary();
dd.setName("value");
dataDictionaryDao.save(dd);

调用save方法时,会先将实体DataDictionary的wubiCode赋值为"value的五笔"、pinYinCode赋值为"value的拼音"后在调用dataDictionaryDao自己本身的save方法。但要记得,使用@ConvertCode需要把org.exitsoft.orm.core.spring.data.jpa.factory.BasicRepositoryFactoryBean类配置到jpa:repositories标签的factory-class属性中.

@ConvertCode属性说明:

属性名称 属性类型 描述
fromProperty java.lang.String 要转码的字段名,意思是根据哪个字段来转换值,如DataDictionary类就拿name作为转换值
convertPropertys org.exitsoft.orm.strategy.annotation.ConvertProperty[] 需要转码的字段注解,@ConvertProperty会在下面说明
executeMehtod org.exitsoft.orm.enumeration.ExecuteMehtod 需要在执行什么方法时做转码工作,ExecuteMehtod是个枚举
	public enum ExecuteMehtod {
		Insert,//在插入时
		Update,//在更新时
		Save;//在保存时(插入或更新时)
	}

@ConvertProperty属性说明:

属性名称 属性类型 描述
propertyNames java.lang.String[] 被转码的字段名,意思是要在类中用那个字段来转码,如DataDictionary类就拿wubiCode和pinyinCode来转码
strategyClass java.lang.Class 转码策略实现类,CodeStrategy接口实体类,CodeStrategy接口会在下面说明
CodeStrategy接口说明

该接口是专门的转码策略接口,该接口需要实现一个方法public Object convertCode(Object value,String propertyName),在转码过程中通过该方法的返回值赋值到被转码的字段中,该方法的调用次数取决与,@ConvertProperty的propertyNames数组,如果propertyNames存在两个值,那么就调用两次,如DataDictionary配置了

@ConvertCode(
    convertPropertys={
            @ConvertProperty(propertyNames={"wubiCode","pinYinCode"},
            strategyClass=PinYinWuBiConvertStrategy.class)
    },
    fromProperty="name",
    executeMehtod=ExecuteMehtod.Save
)

那么public Object convertCode(Object value,String propertyName)在调用BasicHibernateDao的save方法时就会执行两次,如果DataDictionary的name值等于"dd"那么会调用convertCode("dd","pinyinCode")一次,convertCode("dd","wubiCode")一次。 CodeStrategy源码如下:

public interface CodeStrategy {
	
	/**
	 * 通过该方法将 orm 对象的值和属性名传入进行转码
	 * 
	 * @param value 值
	 * @param propertyName 被转码的属性名称
	 */
	public Object convertCode(Object value,String propertyName);
	
}

###对spring data jpa的PageableArgumentResolver的改动. 在使用PageableArgumentResolver做spring mvc的Pageable绑定时,发现一个问题.当我传入页数为1的时候.PageableArgumentResolver有那么一段话:

public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {

	if (methodParameter.getParameterType().equals(Pageable.class)) {

		assertPageableUniqueness(methodParameter);

		Pageable request = getDefaultFromAnnotationOrFallback(methodParameter);
		ServletRequest servletRequest = (ServletRequest) webRequest.getNativeRequest();
		PropertyValues propertyValues = new ServletRequestParameterPropertyValues(servletRequest,
				getPrefix(methodParameter), separator);

		DataBinder binder = new ServletRequestDataBinder(request);

		binder.initDirectFieldAccess();
		binder.registerCustomEditor(Sort.class, new SortPropertyEditor("sort.dir", propertyValues));
		binder.bind(propertyValues);

		if (request.getPageNumber() > 0) {
			request = new PageRequest(request.getPageNumber() - 1, request.getPageSize(), request.getSort());
		}

		return request;
	}

	return UNRESOLVED;
}

if (request.getPageNumber() > 0) {
	request = new PageRequest(request.getPageNumber() - 1, request.getPageSize(), request.getSort());
}

当我传入页数为1的数字时.Pageable的number将会永远都是0,而在下面有一段话.

private static Pageable getDefaultPageRequestFrom(PageableDefaults defaults) {

	// +1 is because we substract 1 later(加1,因为我们会减一)
	int defaultPageNumber = defaults.pageNumber() + 1;
	int defaultPageSize = defaults.value();

	if (defaults.sort().length == 0) {
		return new PageRequest(defaultPageNumber, defaultPageSize);
	}

	return new PageRequest(defaultPageNumber, defaultPageSize, defaults.sortDir(), defaults.sort());
}

至于为什么要加1又减1,这个问题.我还不明白spring为什么要这样做.为了避免我能够传入页数为1的分页.对于该类做了扩展.就把他的加1,减1,去掉.在spring mvc配置文件中配置即可:

<mvc:annotation-driven>
	<mvc:argument-resolvers>
		<bean class="org.exitsoft.orm.core.spring.data.web.PageableArgumentResolver" />
	</mvc:argument-resolvers>
</mvc:annotation-driven>

###spring data jpa的Specification<T>的扩展

对于以上两个功能(状态删除和转码),如果不需要使用时候就不需要配置jpa:repositories标签的factory-class属性,当去掉该属性后,其实一样也能够使用PropertyFilter进行查询和分页操作.

spring data jpa对于动态查询提供了一个非常规范的接口:Specification<T>,主要实现他的toPredicate方法就能够自定义你的查询语句.对于PropertyFilter和直接用属性名查询,exitsoft orm提供了两个实现类:PropertySpecification,PropertyFilterSpecification.如:

@Entity
@Table(name="TB_DATA_DICTIONARY")
public class DataDictionary extends UniversallyUniqueIdentifier{
	
	//名称
	private String name;
	//值
	private String value;
	//类型
	private String type = "S";
	//备注
	private String remark;
	//所属类别
	public DictionaryCategory category;
	//五笔编码
	private String wubiCode;
	//拼音编码
	private String pinYinCode;
	//状态,1:启用,2:禁用,3:删除
	private Integer state;

	//----------GETTER/SETTER-----------//
}

//注意,继承的是JpaRepository和JpaSpecificationExecutor
public interface DataDictionaryDao extends JpaRepository<DataDictionary, String>, JpaSpecificationExecutor<DataDictionary> {

}

//通过属性名查询
dataDictionaryDao.findAll(Specifications.get("name","test"));
//通过属性名查询,但使用条件约束
dataDictionaryDao.findAll(Specifications.get("state","3",restrictionNames.NE));

//通过表达式查询
dataDictionaryDao.findAll(Specifications.get("EQS_name","test"));
//通过表达式查询
dataDictionaryDao.findAll(Specifications.get("NEI_state","3"));

List<PropertyFilter> filters = Lists.newArrayList(
	PropertyFilters.build("LIKES_name", "t"),
	PropertyFilters.build("NEI_state", "3")
);

//多条件查询
dictionaryCategoryDao.findAll(Specifications.get(filters));
//sql:select from TB_DATA_DICTIONARY where state <> ? and name like ?;

//分页查询
Pageable pageable = new PageRequest(0, 10);
Specification<DataDictionary> s = Specifications.get(filters);
Page<DataDictionary> page = dictionaryCategoryDao.findAll(pageable,s);

###扩展表达式的约束名称

在封装过程中,本人对一些常用的约束名称已经写好。如果你在项目开发时觉得表达式里面的约束名称不够用,可以对表达式做扩展处理。扩展约束名称主要关注这几个类:

  1. JpaRestrictionBuilder
  2. PredicateBuilder
  3. PredicateSingleValueSupport
  4. PredicateMultipleValueSupport

在PropertySpecification和PropertyFilterSpecification中,toPredicate方法首先会创建一个SpecificationModel类,该类只要的目的是将Root root, CriteriaQuery query,CriteriaBuilder builder,3个对象封装起来,成为一个载体,在调用JpaRestrictionBuilder的getRestriction方法时将该载体传入到PredicateBuilder接口中.

####PredicateBuilder接口

PredicateBuilder是一个构造Jpa Predicate的类,该类有一个方法是专门提供根据PropertyFilter是如何创建Predicate的:

/**
 * 
 * 辅助JpaRestrictionBuilder类创建PropertyFilter后,
 * 使用哪种约束条件向CriteriaBuilder添加Predicate进行条件过滤查询的接口
 * 
 * @author vincent
 *
 */
public interface PredicateBuilder {

	/**
	 * 获取Jpa的约束标准
	 * 
	 * @param filter 属性过滤器
	 * @param model jpa绑定模型
	 * 
	 * @return Predicate
	 * 
	 */
	public Predicate build(PropertyFilter filter,SpecificationModel model);
	
	/**
	 * 获取Predicate标准的约束名称
	 * 
	 * @return String
	 */
	public String getRestrictionName();
	
	/**
	 * 获取Jpa的约束标准
	 * 
	 * @param propertyName 属性名
	 * @param value 值
	 * @param builder CriteriaBuilder
	 * 
	 * @return Predicate
	 * 
	 */
	public Predicate build(String propertyName, Object value,SpecificationModel model);
}

而PredicateBuilder有两个实现某些方法的抽象类:PredicateMultipleValueSupport和PredicateSingleValueSupport

####PredicateSingleValueSupport抽象类 该类是PredicateBuilder的子类,该类实现了build(PropertyFilter filter,SpecificationModel model);实现体主要是对PropertyFilter的值模型做处理。并且逐个循环调用Predicate build(String propertyName, Object value,SpecificationModel model);方法给实现体做处理:

/**
 * 处理PropertyFilter#getMatchValue()的基类,本类对3种值做处理
 * 
 * 1.值等于正常值的,如:"amdin",会产生的squall为:property = 'admin'
 * 2.值等于或值的,如:"admin_OR_vincent",会产生的sql为:property = 'admin' or property = 'vincent'
 * 3.值等于与值的,如:"admin_AND_vincent",会产生的sql为:property = 'admin' and property = 'vincent'
 * 
 * @author vincent
 *
 */
public abstract class PredicateSingleValueSupport implements PredicateBuilder{
	
	//or值分隔符
	private String orValueSeparator = "|";
	//and值分隔符
	private String andValueSeparator = ",";
	
	public PredicateSingleValueSupport() {
		
	}
	

	public Predicate build(PropertyFilter filter,SpecificationModel model) {

		String matchValue = filter.getMatchValue();
		Class<?> propertyType = filter.getPropertyType();
		
		MatchValue matchValueModel = getMatchValue(matchValue, propertyType);
		
		Predicate predicate = null;
		
		if (matchValueModel.hasOrOperate()) {
			predicate = model.getBuilder().disjunction();
		} else {
			predicate = model.getBuilder().conjunction();
		}
		
		for (Object value : matchValueModel.getValues()) {
			if (filter.hasMultiplePropertyNames()) {
				for (String propertyName:filter.getPropertyNames()) {
					predicate.getExpressions().add(build(propertyName, value, model));
				}
			} else {
				predicate.getExpressions().add(build(filter.getSinglePropertyName(), value, model));
			}
		}
		
		return predicate;
	}
	
	public Predicate build(String propertyName, Object value,SpecificationModel model) {
		
		return build(Specifications.getPath(propertyName, model.getRoot()),value,model.getBuilder());
	}
	
	/**
	 * 
	 * 获取Jpa的约束标准
	 * 
	 * @param expression 属性路径表达式
	 * @param value 值
	 * @param builder CriteriaBuilder
	 * 
	 * @return Predicate
	 */
	public abstract Predicate build(Path<?> expression,Object value,CriteriaBuilder builder);
	
	/**
	 * 获取值对比模型
	 * 
	 * @param matchValue 值
	 * @param propertyType 值类型
	 * 
	 * @return MatchValue
	 */
	public MatchValue getMatchValue(String matchValue,Class<?> propertyType) {
		return MatchValue.createMatchValueModel(matchValue, propertyType,andValueSeparator,orValueSeparator);
	}

	/**
	 * 获取and值分隔符
	 * 
	 * @return String
	 */
	public String getAndValueSeparator() {
		return andValueSeparator;
	}

	/**
	 * 设置and值分隔符
	 * @param andValueSeparator and值分隔符
	 */
	public void setAndValueSeparator(String andValueSeparator) {
		this.andValueSeparator = andValueSeparator;
	}
}

PredicateMultipleValueSupport抽象类

该类是CriterionSingleValueSupport的子类。重写了CriterionSingleValueSupport类的public Criterion build(PropertyFilter filter)和public Criterion build(String propertyName, Object value)。并且添加了一个抽象方法public abstract Criterion buildRestriction(String propertyName,Object[] values)。该类主要作用是在多值的情况不逐个循环,而是全部都将参数组合成一个数组传递给抽象方法buildRestriction(String propertyName,Object[] values)中。这种情况在in或not in约束中就用得到。

/**
 * 对PropertyFilter#getMatchValue()的特殊情况值做处理,例如 in, not in, between的多值情况,
 * 该类值处理一种情况
 * 
 * 	例如:
 * 
 * INI_property = "1,2,3,4";
 * 会产生的sql为: property in (1,2,3,4)
 * 
 * @author vincent
 *
 */
public abstract class PredicateMultipleValueSupport extends PredicateSingleValueSupport{
	
	/**
	 * 将得到值与指定分割符号,分割,得到数组
	 *  
	 * @param value 值
	 * @param type 值类型
	 * 
	 * @return Object
	 */
	public Object convertMatchValue(String value, Class<?> type) {
		Assert.notNull(value,"值不能为空");
		String[] result = StringUtils.splitByWholeSeparator(value, getAndValueSeparator());
		
		return  ConvertUtils.convertToObject(result,type);
	}
	
	public Predicate build(PropertyFilter filter, SpecificationModel model) {
		Object value = convertMatchValue(filter.getMatchValue(), filter.getPropertyType());
		Predicate predicate = null;
		
		if (filter.hasMultiplePropertyNames()) {
			Predicate orDisjunction = model.getBuilder().disjunction();
			for (String propertyName:filter.getPropertyNames()) {
				orDisjunction.getExpressions().add(build(propertyName,value,model));
			}
			predicate = orDisjunction;
		} else {
			predicate = build(filter.getSinglePropertyName(),value,model);
		}
		
		return predicate;
	}
	
	public Predicate build(Path<?> expression, Object value,CriteriaBuilder builder) {
		return buildRestriction(expression,(Object[])value,builder);
	}
	
	/**
	 * 获取Jpa的约束标准
	 * 
	 * @param expression root路径
	 * @param values 值
	 * @param builder CriteriaBuilder
	 * 
	 * @return Predicate
	 */
	public abstract Predicate buildRestriction(Path<?> expression,Object[] values,CriteriaBuilder builder);
}

JpaRestrictionBuilder jpa约束捆绑者

JpaRestrictionBuilder是装载所有PredicateBuilder子类的包装类,该类有一块静态局域。去初始化所有的约束条件。并提供两个方法去创建Jap的Predicate,该类是PropertyFilterSpecification和PropertySpecification类中条件表达式查询的关键类。所有通过条件创建的Predicate都是通过该类创建。

/**
 * jpa约束捆绑者,将所有的PredicateBuilder实现类添加到getRestrictionsMap()中,
 * 辅佐PropertyFilterSpecification和RestrictionNameSpecification
 * 做创建Predicate操作。
 * 
 * @author vincent
 *
 */
public class JpaRestrictionBuilder{
	
	private static Map<String, PredicateBuilder> predicateBuilders = new HashMap<String, PredicateBuilder>();
	
	static {
		PredicateBuilder eqRestriction = new EqRestriction();
		PredicateBuilder neRestriction = new NeRestriction();
		PredicateBuilder geRestriction = new GeRestriction();
		PredicateBuilder gtRestriction = new GtRestriction();
		PredicateBuilder inRestriction = new InRestriction();
		PredicateBuilder lLikeRestriction = new LLikeRestriction();
		PredicateBuilder leRestriction = new LeRestriction();
		PredicateBuilder likeRestriction = new LikeRestriction();
		PredicateBuilder ltRestriction = new LtRestriction();
		PredicateBuilder notInRestriction = new NinRestriction();
		PredicateBuilder rLikeRestriction = new RLikeRestriction();
		
		predicateBuilders.put(eqRestriction.getRestrictionName(), eqRestriction);
		predicateBuilders.put(neRestriction.getRestrictionName(), neRestriction);
		predicateBuilders.put(geRestriction.getRestrictionName(), geRestriction);
		predicateBuilders.put(inRestriction.getRestrictionName(), inRestriction);
		predicateBuilders.put(gtRestriction.getRestrictionName(), gtRestriction);
		predicateBuilders.put(lLikeRestriction.getRestrictionName(), lLikeRestriction);
		predicateBuilders.put(leRestriction.getRestrictionName(), leRestriction);
		predicateBuilders.put(likeRestriction.getRestrictionName(), likeRestriction);
		predicateBuilders.put(ltRestriction.getRestrictionName(), ltRestriction);
		predicateBuilders.put(rLikeRestriction.getRestrictionName(), rLikeRestriction);
		predicateBuilders.put(notInRestriction.getRestrictionName(), notInRestriction);
	}
	
	/**
	 * 通过属性过滤器创建Predicate
	 * 
	 * @param filter 属性过滤器
	 * @param restrictionModel jpa查询绑定载体
	 * 
	 * @return Predicate
	 */
	public static Predicate getRestriction(PropertyFilter filter,SpecificationModel model) {
		if (!predicateBuilders.containsKey(filter.getRestrictionName())) {
			throw new IllegalArgumentException("找不到约束名:" + filter.getRestrictionName());
		}
		PredicateBuilder predicateBuilder  = predicateBuilders.get(filter.getRestrictionName());
		return predicateBuilder.build(filter,model);
	}

	/**
	 * 通过属性名称,值,约束条件创建Predicate
	 * 
	 * @param propertyName 属性名称
	 * @param value 值
	 * @param restrictionName 约束条件
	 * @param model jpa查询绑定载体
	 * 
	 * @return Predicate
	 */
	public static Predicate getRestriction(String propertyName, Object value,String restrictionName,SpecificationModel model) {
		if (!predicateBuilders.containsKey(restrictionName)) {
			throw new IllegalArgumentException("找不到约束名:" + restrictionName);
		}
		PredicateBuilder predicateBuilder  = predicateBuilders.get(restrictionName);
		return predicateBuilder.build(propertyName, value, model);
	}

	/**
	 * 获取所有的条件约束
	 * 
	 * @return Map
	 */
	public static Map<String, PredicateBuilder> getPredicateBuilders() {
		return predicateBuilders;
	}

	/**
	 * 设置所有的条件约束
	 * 
	 * @param 条件约束
	 */
	public static void setPredicateBuilders(Map<String, PredicateBuilder> predicateBuilders) {
		JpaRestrictionBuilder.predicateBuilders = predicateBuilders;
	}
	
	
}

了解完以上几个类。那么假设现在有个需求。要写一个模糊约束 (rom object o where o.value like '%?%')来判断一些值,可以通过继承PredicateSingleValueSupport类,实现build(Path<?> expression,Object value,CriteriaBuilder builder),如:

/**
 * 模糊约束 ( from object o where o.value like '%?%') RestrictionName:LIKE
 * <p>
 * 表达式:LIKE属性类型_属性名称[_OR_属性名称...]
 * </p>
 * 
 * @author vincent
 *
 */
public class LikeRestriction extends PredicateSingleValueSupport{
	
	public String getRestrictionName() {
		return RestrictionNames.LIKE;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Predicate build(Path expression, Object value,CriteriaBuilder builder) {
		
		return builder.like(expression, "%" + value + "%");
	}

	

}

通过某种方式(如spring的InitializingBean,或serlvet)将该类添加到JpaRestrictionBuilder的PredicateBuilder中。就可以使用约束名了。

PredicateBuilder nlikeRestriction= new NlikeRestriction();
JpaRestrictionBuilder.getCriterionMap().put(nlikeRestriction.getRestrictionName(), nlikeRestriction);

以上是exitsoft orm的使用说明。该jar在vcs-admin-jpa演示例子中的org.exitsoft.showcase.vcsadmin.dao包下都使用到。可以参考例子。