001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019*/ 020 021package org.granite.tide.spring.data; 022 023import java.beans.BeanInfo; 024import java.beans.Introspector; 025import java.beans.PropertyDescriptor; 026import java.util.ArrayList; 027import java.util.List; 028 029import javax.persistence.criteria.CriteriaBuilder; 030import javax.persistence.criteria.CriteriaQuery; 031import javax.persistence.criteria.Path; 032import javax.persistence.criteria.Predicate; 033import javax.persistence.criteria.Root; 034import javax.persistence.metamodel.Attribute; 035import javax.persistence.metamodel.ManagedType; 036import javax.persistence.metamodel.Metamodel; 037 038import org.springframework.data.jpa.domain.Specification; 039 040public class FilterExampleSpecification<T> implements Specification<T> { 041 042 private Metamodel metamodel; 043 private Object filter; 044 045 private FilterExampleSpecification(Metamodel metamodel, Object filter) { 046 this.metamodel = metamodel; 047 this.filter = filter; 048 } 049 050 public static <T> FilterExampleSpecification<T> byExample(Metamodel metamodel, Object filter) { 051 return new FilterExampleSpecification<T>(metamodel, filter); 052 } 053 054 public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { 055 List<Predicate> predicates = new ArrayList<Predicate>(); 056 057 applyAttributes(predicates, root, builder, metamodel.entity(filter.getClass()), filter); 058 059 if (predicates.size() > 0) 060 return builder.and(predicates.toArray(new Predicate[predicates.size()])); 061 062 return null; 063 } 064 065 private void applyAttributes(List<Predicate> predicates, Path<?> root, CriteriaBuilder builder, ManagedType<?> filterType, Object filter) { 066 // Query by example : the filter is of the same type as the entity 067 PropertyDescriptor[] pds = null; 068 try { 069 // Query by bean filter 070 BeanInfo info = Introspector.getBeanInfo(filter.getClass()); 071 pds = info.getPropertyDescriptors(); 072 } 073 catch (Exception e) { 074 throw new RuntimeException("Could not introspect filter bean", e); 075 } 076 077 for (PropertyDescriptor pd : pds) { 078 Attribute<?, ?> attribute = filterType.getAttribute(pd.getName()); 079 if (attribute == null) 080 continue; 081 082 if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED) { 083 // Visit embedded elements recursively 084 try { 085 Object embedded = pd.getReadMethod().invoke(filter); 086 if (embedded != null) { 087 ManagedType<?> embeddedType = metamodel.embeddable(attribute.getJavaType()); 088 applyAttributes(predicates, root.get(pd.getName()), builder, embeddedType, embedded); 089 } 090 } 091 catch (Exception e) { 092 throw new RuntimeException("Could not get filter property " + pd.getName(), e); 093 } 094 095 continue; 096 } 097 098 if (attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) 099 continue; 100 101 Object value = null; 102 try { 103 value = pd.getReadMethod().invoke(filter); 104 } 105 catch (Exception e) { 106 throw new RuntimeException("Could not get filter property " + pd.getName(), e); 107 } 108 109 Predicate predicate = FilterSpecUtil.buildPredicate(root, builder, pd.getReadMethod().getReturnType(), pd.getName(), value); 110 if (predicate != null) 111 predicates.add(predicate); 112 } 113 } 114}