spring-data query - hql

public interface AreaRepository extends JpaRepository<Area, Integer>, JpaSpecificationExecutor<Area>{
#Query("from Area where sup is null")
List<Area> findProvinces();
#Query("from Area where sup is null")
Page<Area> findProvinces(Pageable pg);
}
Here is my code. The first method works fine but the second one doesn't. Can anyone tell me how to make it correct?

here doesn't work mean the second query throws an error and can't find out all the data specified by my sql
#Query("from Area where sup is null")
.
Actually what i want to archieve is a qbe pattern using jpa,and i finally got a solution implementing org.springframework.data.jpa.domain.Specification interface.
public class QbeSpec<T> implements Specification<T> {
private final T example;
public QbeSpec(T example) {
this.example = example;
}
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if (example == null) {
return cb.isTrue(cb.literal(true));
}
BeanInfo info = null;
try {
info = Introspector.getBeanInfo(example.getClass());
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
List<Predicate> predicates = new ArrayList<Predicate>();
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
String name = pd.getName();
Object value = null;
if (name.equals("class"))
continue;
try {
value = pd.getReadMethod().invoke(example);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (value != null) {
Path<String> path = root.get(name);
// string using like others using equal
if (pd.getPropertyType().equals(String.class)) {
predicates.add(cb.like(path, "%" + value.toString() + "%"));
} else {
predicates.add(cb.equal(path, value));
}
}
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}

Related

Hibernate mapping: Get collection on runtime with nullSafeGet overrided method

To make the mapping between hibernate and my database work, I have this mapping :
<property name="userRolesV2" column="user_roles_v2">
<type name="io.markethero.repository.CommaSeparatedGenericEnumType">
<param name="enumClassName">io.markethero.model.UserLoginRoleV2</param>
<param name="collectionClassName">java.util.Set</param>
</type>
</property>
The idea is to directly get the Collection in my field class instead of doing the mapping each time.
For example, the field in the class could be a Set, a List, or a Queue.
In the database, the value is like "enumValue1,enumValue2,enumValue3".
To do that, my class CommaSeparatedGenericEnumType is like this:
public class CommaSeparatedGenericEnumType implements UserType, ParameterizedType {
private Class enumClass = null;
private Class targetCollection = null;
public void setParameterValues(Properties params) {
String enumClassName = params.getProperty("enumClassName");
String collectionClassName = params.getProperty("collectionClassName");
if (enumClassName == null) {
throw new MappingException("enumClassName parameter not specified");
}
if (collectionClassName == null) {
throw new MappingException("collectionClassName parameter not specified");
}
try {
this.enumClass = Class.forName(enumClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("enumClass " + enumClassName + " not found", e);
}
try {
this.targetCollection = Class.forName(collectionClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("targetCollection " + collectionClassName + " not found", e);
}
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
List<Object> result = new ArrayList<>();
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
result.add(Enum.valueOf(enumClass, string));
}
}
return result;
}
#Override
#SuppressWarnings("unchecked")
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
List<Object> enums = (List) value;
StringBuilder sb = new StringBuilder("");
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
if (sb.toString().isEmpty()) {
st.setNull(index, Types.VARCHAR);
} else {
String commaSeparatedIds = sb.toString().substring(0, sb.toString().length() - 1);
st.setString(index, commaSeparatedIds);
}
}
}
}
I would like to be able to parametrize which collection nullSafeGet and nullSafeSet are going to use, because for now, it's only working with a list.
Thank you!
Maybe my question was asked was oddly, but here what I did:
For the getter, I used the factory pattern already implemented by Spring : CollectionFactory.
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
Collection<Object> result = CollectionFactory.createCollection(this.targetCollection, 50);
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
try {
result.add(Enum.valueOf(enumClass, string));
} catch (IllegalArgumentException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeGet] No such enum value"+ string +"for enum : " + enumClass, e);
}
}
}
return result;
}
For the setter, I used the reflection API.
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
} else {
if (value instanceof Collection) {
try {
StringBuilder sb = new StringBuilder("");
Constructor<?> c = value.getClass().getConstructor(Collection.class);
Collection<Object> enums = (Collection<Object>) c.newInstance((Collection<Object>) value);
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
String commaSeparatedIds = sb.substring(0, sb.toString().length() - 1);
if (sb.length() > 0) {
st.setString(index, commaSeparatedIds);
} else {
st.setNull(index, Types.VARCHAR);
}
} catch (NoSuchMethodException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] No such constructor found for class : " + value.getClass(), e);
} catch (InstantiationException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] Class : " + value.getClass() + " cannot be instantiate ", e);
} catch (IllegalAccessException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] You cannot access to constructor of class : " + value.getClass(), e);
} catch (InvocationTargetException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] InvocationTargetException for class : " + value.getClass(), e);
}
} else {
st.setNull(index, Types.VARCHAR);
throw new IllegalArgumentException();
}
}
}

How to load markers instantly android

I using googleMap and what i'm tring to do is to refresh markers from firebase database, and that mean i delete all the previous markers and add the new ones by using this methode
#Override
public void onKeyEntered(String key, GeoLocation location) {
try{
MyItem myItem = new MyItem(location.latitude, location.longitude);
if (!items.contains(myItem)) {
items.add(myItem);
}
Log.d("onKey","called");
}catch (ClassCastException e){
Log.d("classCastException","");
}
}
parseJsonToList methode :
private void parseJsonToList() {
itemss = clusterManagerAlgorithm.getItems();
try {
items.removeAll(itemss);
}catch (IndexOutOfBoundsException e){
Log.d("itemsDoesn't exist"," : ");
}
mClusterManager.clearItems();
mClusterManager.cluster();
mClusterManager.addItems(items);
Log.d("items"," : " + items);
}
So, my problem is that everytime I'm tring to clean and load data what happened is that all markers desapears for about 3 second to been reloaded again. How can i delete and reload data without markers dispear.
this is myItem class :
public class MyItem implements ClusterItem {
private final LatLng mPosition;
public MyItem(double lat, double lng) {
mPosition = new LatLng(lat, lng);
}
#Override
public LatLng getPosition() {
return mPosition;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyItem item = (MyItem) o;
return Objects.equals(mPosition, item.mPosition);
}
#Override
public int hashCode() {
return Objects.hash(mPosition);
}
}

Usage of custom freemarker template

I was wondering if anyone can help me with Apache FreeMarker? I'm trying to use a custom model but I can't figure it out.
Imagine I want to dump the result of a query (java ResultSet in a FreeMarker template). What is the best approach?
I have found on Google the class: ResultSetTemplateModel
import java.sql.ResultSet;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateSequenceModel;
public class ResultSetTemplateModel implements TemplateSequenceModel {
private ResultSet rs = null;
public ResultSetTemplateModel(ResultSet rs) {
this.rs = rs;
}
public TemplateModel get(int i) throws TemplateModelException {
try {
rs.next();
} catch(Exception e) {
throw new TemplateModelException(e.toString());
}
TemplateModel model = new Row(rs);
return model;
}
public int size() throws TemplateModelException {
int size=0;
try {
rs.last();
size = rs.getRow();
rs.beforeFirst();
} catch (Exception e ) {
throw new TemplateModelException( e.toString());
}
return size;
}
class Row implements TemplateHashModel {
private ResultSet rs = null;
public Row(ResultSet rs) {
this.rs = rs;
}
public TemplateModel get(String s) throws TemplateModelException {
TemplateModel model = null;
try {
model = new SimpleScalar( rs.getString(s) );
} catch (Exception e) { e.printStackTrace(); }
return model;
}
public boolean isEmpty() throws TemplateModelException {
boolean isEmpty = false;
if ( rs == null ) { isEmpty = true; }
return isEmpty;
}
}
}
And I have a very simple class (I even made it easier than previous):
public static void main(String[] args) {
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_27);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setClassForTemplateLoading(MyCLASS.class, "/");
StringWriter out = new StringWriter();
Map<String, Object> parameters = new TreeMap<>();
ResultSet rs = getResultSet("Select foo, bar FROM my_table");
parameters.put("hello", "World");
parameters.put("result", rs);
Template temp = cfg.getTemplate("template.txt");
temp.process(parameters, out);
System.out.println("out = " + out);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
My template
Hello ${hello}
<#-- how do I specify ResultSet columns here ?? -->
How can I use the custom template?? Any advice?? I know how to load the template file. But I don't know how to specify that it is a custom model in the template.
THank you guys for the support :)
There are two ways of using ResultSetTemplateModel for wrapping ResultSet-s:
Either extend DefaultObjectWrapper by overriding handleUnknownType, where you return new ResultSetTemplateModel((ResultSet) obj) if obj is a ResultSet, otherwise call super. Then use Configuration.setObjectWrapper to actually use it.
Or, add new ResultSetTemplate(rs) to parameters instead of rs; if something is already a TempalteModel, it will not be wrapped again. Note that if you get a ResultSet from somewhere else in the template, this approach will not work as it avoids your manual wrapping, so extending the DefaultObjectWrapper is what you want generally.
Note that the ResultSetTemplateModel implementation shown is quite limited. The ObjectWrapper should be passed to the constructor as well, and stored in a final field. Then, instead of new SimpleScalar( rs.getString(s) ) it should do objectWrapper.wrap(rs.getObject(s)).

spring data jpa dynamic query which has IN clause

I want to create dynamic query in spring data jpa. Doing many search I can implement it, but I came across a problem when I add IN operator in where clause. I need to check id IN (longlist)
Here is my entity class
#Entity
#Table(name = "view_detail")
public class ViewDetailDom {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
#Column(name = "user_id")
private Long userId;
private String description;
Here is specification builder class and specification class
public class ViewDetailSpecificationsBuilder {
private final List<SearchCriteria> params;
public ViewDetailSpecificationsBuilder() {
params = new ArrayList<SearchCriteria>();
}
public ViewDetailSpecificationsBuilder with(String key, Operation operation, Object value) {
params.add(new SearchCriteria(key, operation, value));
return this;
}
public Specification<ViewDetailDom> build() {
if (params.size() == 0) {
return null;
}
List<Specification<ViewDetailDom>> specs = new ArrayList<Specification<ViewDetailDom>>();
for (SearchCriteria param : params) {
specs.add(new ViewDetailSpecification(param));
}
Specification<ViewDetailDom> result = specs.get(0);
for (int i = 1; i < specs.size(); i++) {
result = Specifications.where(result).and(specs.get(i));
}
return result;
}
}
public class ViewDetailSpecification implements Specification<ViewDetailDom> {
private SearchCriteria criteria = new SearchCriteria();
public ViewDetailSpecification(SearchCriteria searchCriteria) {
this.criteria.setKey(searchCriteria.getKey());
this.criteria.setOperation(searchCriteria.getOperation());
this.criteria.setValue(searchCriteria.getValue());
}
#Override
public Predicate toPredicate(Root<ViewDetailDom> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
String value = criteria.getValue().toString().replaceAll(" ", "%");
if (criteria.getOperation() != null && criteria.getOperation() != Operation.DEFAULT) {
if (criteria.getOperation() == Operation.GREATHERTHANEQUALTO) {
return builder.greaterThanOrEqualTo(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.LESSTHANEQUALTO) {
return builder.lessThanOrEqualTo(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.EQUAL) {
return builder.equal(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.IN) {
Path<Long> view = root.<Long>get(criteria.getKey());
return view.in(criteria.getValue());
}
} else {
if (root.get(criteria.getKey()).getJavaType() == String.class) {
return builder.like(builder.lower(root.<String>get(criteria.getKey())),
"%" + value.toLowerCase() + "%");
} else {
return builder.equal(root.get(criteria.getKey()), value);
}
}
return null;
}
}
This method creates specification builder:
public ViewDetailSpecificationsBuilder createSearchSpecifications(ViewSearch view) {
ViewDetailSpecificationsBuilder builder = new ViewDetailSpecificationsBuilder();
if (StringUtils.isNotBlank(view.getName())) {
builder.with("name", Operation.DEFAULT, view.getName());
}
if (StringUtils.isNotBlank(view.getDescription())) {
builder.with("description", Operation.DEFAULT, view.getDescription());
}
return builder;
}
And finally I do this:
ViewDetailSpecificationsBuilder builder = createSearchSpecifications(view);
builder.with("userId", Operation.DEFAULT, userSessionHelper.getUserId());
builder.with("id", Operation.IN, viewids);
Specification<ViewDetailDom> spec = builder.build();
viewDetailDao.findAll(spec);
But I am getting following error:
"Unaware how to convert value [[5, 7, 8] : java.util.ArrayList] to requested type [java.lang.Long]; nested exception is java.lang.IllegalArgumentException: Unaware how to convert value [[5, 7, 8] : java.util.ArrayList] to requested type [java.lang.Long]"
I have resolved this problem in this way:
ViewDetailSpecification class:
if (criteria.getOperation() == Operation.IN) {
final List<Predicate> orPredicates = new ArrayList<Predicate>();
List<Long> viewIds = (List<Long>) criteria.getValue();
for (Long viewid : viewIds) {
orPredicates.add(builder.or(builder.equal(root.<String>get(criteria.getKey()), viewid)));
}
return builder.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
}
In kotlin I have the same error, I change the ArrayList to Array, with this code:
fun values(): Array<String> {
val elems = arrayListOf<String>()
return elems.toTypedArray()
}
Try you convert ArrayList to array, for java see: make arrayList.toArray() return more specific types

Spring Jpa Specification and Eager loading

How can i get list of tiket with its categories through Jpa Specification
Example model:
#Entity
#Table(name = "tickets")
public class Ticket {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private Category
}
Method of service:
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.join("category");
return query.getRestriction();
}, pageable);
}
I was able to eager load the collection by using a fetch instead of a join.
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.fetch("category");
return query.getRestriction();
}, pageable);
}
The fetch method will use the default join type (inner). If want to load tickets with no category, you'll have to pass JoinType.LEFT as the second parameter.
as they say, stackoverflow giveth, stackoverflow taketh..
I took this class off SO quite some time ago, feel free to recycle it..
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Hibernate;
public class BeanLoader {
/**
* StackOverflow safe, if called before json creation, cyclic object must be avoided
*/
public static void eagerize(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, new ArrayList<String>());
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, new ArrayList<String>());
}
}
}
}
}
/**
* StackOverflows if passed a bean containing cyclic fields. Call only if sure that this won't happen!
*/
public static void eagerizeUnsafe(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp);
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item);
}
}
}
}
}
private static void eagerize(Object obj, ArrayList<String> visitedBeans) {
if (!visitedBeans.contains(obj.getClass().getName())){
visitedBeans.add(obj.getClass().getName());
} else {
return;
}
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, visitedBeans);
}
if (origProp instanceof User){
((User) origProp).setRelatedNra(null);
User u=(User) origProp;
if (u.getRelatedMps()!=null)
u.getRelatedMps().clear();
if (u.getRelatedDps()!=null)
u.getRelatedDps().clear();
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, (ArrayList<String>) visitedBeans.clone());
}
}
}
}
}
}
modify it as you see fit.. the method you'll call is "eagerizeUnsafe".
YMMV, but this should to the trick to eagerize all the collections of a lazily initialized bean.

Resources