/*
* @(#)JPQL.java 2013-4-1 下午23:33:33
*
* Copyright (c) 2011-2013 Makersoft.org all rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
*
*/
package org.makersoft.activerecord.jpa;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import org.apache.commons.lang3.StringUtils;
import org.makersoft.activerecord.ActiveRecord;
import org.makersoft.activerecord.hql.JoinParser;
import org.makersoft.activerecord.hql.Parser;
import org.makersoft.activerecord.hql.SelectParser;
import org.makersoft.activerecord.hql.WhereParser;
/**
* JPQL
*/
public class JPQL {
private static final String EMPTY_STRING = " ";
private final String entityName;
private final Set<String> columns;
private final String alias;
private String select = EMPTY_STRING;
private String where = EMPTY_STRING;
private String joins = EMPTY_STRING;
private String group = EMPTY_STRING;
private String order = EMPTY_STRING;
private int offset;
private int limit;
private Map<String, Object> parameters = new HashMap<String, Object>();
public static JPQL newInstance(Class<?> clazz){
return new JPQL(clazz);
}
private JPQL(Class<?> clazz){
this.entityName = clazz.getSimpleName();
this.alias = entityName.toLowerCase();
this.columns = this.getColumns();
}
public static EntityManager em() {
return JPA.em();
}
public static long count(Class<?> clazz) {
return Long.parseLong(em().createQuery("select count(*) from " + clazz.getName() + " e")
.getSingleResult().toString());
}
@SuppressWarnings("rawtypes")
public static List findAll(String entity) {
return em().createQuery("select e from " + entity + " e").getResultList();
}
public static <T extends ActiveRecord> T findById(Class<T> entityClass, Long id)
throws Exception {
return em().find(entityClass, id);
}
public static JPAQuery all(String entityName) {
String hql = createFindByQuery(entityName, entityName, null);
Query q = em().createQuery(hql);
return new JPAQuery(hql, bindParameters(q));
}
//conditions
private String parseWhere(String condition) {
Parser parser = new WhereParser(columns, alias);
parser.parse(condition);
return parser.toHQL();
}
private String parse(String condition) {
Parser parser = new SelectParser(columns, alias);
parser.parse(condition);
return parser.toHQL();
}
private String parseJoin(String joins) {
Parser parser = new JoinParser(columns, alias);
parser.parse(joins);
return parser.toHQL();
}
public JPQL from(String from){
return this;
}
public JPQL where(String condition){
this.where = (StringUtils.isBlank(where) ? "where" : where + " and ") + "(" + parseWhere(condition) + ")";
return this;
}
public JPQL where(String hql, Map<String, Object> params){
this.where(hql);
this.parameters.putAll(params);
return this;
}
public JPQL where(Map<String, Object> params){
throw new RuntimeException();
// return this;
}
public JPQL group(String group) {
if (group.startsWith("group")) {
this.group = parse(group);
} else {
this.group = "group by " + parse(group);
}
return this;
}
public JPQL having(String having){
// this.having
return this;
}
public JPQL joins(String joins) {
if (joins.contains("join")) {
this.joins = parseJoin(joins);
} else {
this.joins = " inner join fetch " + parseJoin(joins);
}
return this;
}
public Long count(){
this.select = "select count(*)";
return (Long) single();
}
public Long count(String columns){
return null;
}
@SuppressWarnings("unchecked")
public <T> List<T> list() {
// String hql = select + BLANK_STRING + "from" + BLANK_STRING + entityName + " as " + alias + BLANK_STRING + joins + BLANK_STRING + where + BLANK_STRING + group + BLANK_STRING + order + BLANK_STRING;
StringBuffer hql = new StringBuffer();
hql.append(select);
hql.append(" from ");
hql.append(entityName);
hql.append(" as ");
hql.append(alias);
hql.append(joins);
hql.append(where);
hql.append(group);
hql.append(order);
System.out.println("===" + hql.toString());
Query query = em().createQuery(hql.toString());
bindParameters(query, parameters);
query.setFirstResult(offset);
if (limit > 0) {
query.setMaxResults(limit);
}
return query.getResultList();
}
@SuppressWarnings("unchecked")
public <T> T single(){
StringBuffer hql = new StringBuffer();
hql.append(select);
hql.append(" from ");
hql.append(entityName);
hql.append(" as ");
hql.append(alias);
hql.append(joins);
hql.append(where);
hql.append(group);
hql.append(order);
Query query = em().createQuery(hql.toString());
bindParameters(query, parameters);
return (T)query.getSingleResult();
}
public JPQL select(String columns) {
this.select = "select " + parse(columns);
return this;
}
public JPQL order(String columns) {
this.order = "order by " + parse(columns);
return this;
}
public JPQL offset(int offset){
this.offset = offset;
return this;
}
public JPQL limit(Integer limit){
this.limit = limit;
return this;
}
//private method
@SuppressWarnings({ "rawtypes", "unchecked" })
private Set<String> getColumns() {
Metamodel metamodel = em().getMetamodel();
Set<String> columns = new HashSet<String>();
for(EntityType entityType : metamodel.getEntities()){
if (entityName.equals(entityType.getName())) {
Set<Attribute> attributes = entityType.getAttributes();
for (Attribute attribute : attributes) {
columns.add(attribute.getName());
}
}
}
return columns;
}
private static String createFindByQuery(String entityName, String entityClass, String query, Object... params) {
if (query == null || query.trim().length() == 0) {
return "from " + entityName;
}
if (query.matches("^by[A-Z].*$")) {
return "from " + entityName + " where " + findByToJPQL(query);
}
if (query.trim().toLowerCase().startsWith("select ")) {
return query;
}
if (query.trim().toLowerCase().startsWith("from ")) {
return query;
}
if (query.trim().toLowerCase().startsWith("order by ")) {
return "from " + entityName + " " + query;
}
if (query.trim().indexOf(" ") == -1 && query.trim().indexOf("=") == -1 && params != null
&& params.length == 1) {
query += " = ?1";
}
if (query.trim().indexOf(" ") == -1 && query.trim().indexOf("=") == -1 && params == null) {
query += " = null";
}
return "from " + entityName + " where " + query;
}
@SuppressWarnings("unchecked")
public static Query bindParameters(Query q, Object... params) {
if (params == null) {
return q;
}
if (params.length == 1 && params[0] instanceof Map) {
return bindParameters(q, (Map<String, Object>) params[0]);
}
for (int i = 0; i < params.length; i++) {
q.setParameter(i + 1, params[i]);
}
return q;
}
public static Query bindParameters(Query q, Map<String,Object> params) {
if (params == null) {
return q;
}
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
return q;
}
public static String findByToJPQL(String findBy) {
findBy = findBy.substring(2);
StringBuffer jpql = new StringBuffer();
String[] parts = findBy.split("And");
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (part.endsWith("NotEqual")) {
String prop = extractProp(part, "NotEqual");
jpql.append(prop + " <> ?");
} else if (part.endsWith("Equal")) {
String prop = extractProp(part, "Equal");
jpql.append(prop + " = ?");
} else if (part.endsWith("IsNotNull")) {
String prop = extractProp(part, "IsNotNull");
jpql.append(prop + " is not null");
} else if (part.endsWith("IsNull")) {
String prop = extractProp(part, "IsNull");
jpql.append(prop + " is null");
} else if (part.endsWith("LessThan")) {
String prop = extractProp(part, "LessThan");
jpql.append(prop + " < ?");
} else if (part.endsWith("LessThanEquals")) {
String prop = extractProp(part, "LessThanEquals");
jpql.append(prop + " <= ?");
} else if (part.endsWith("GreaterThan")) {
String prop = extractProp(part, "GreaterThan");
jpql.append(prop + " > ?");
} else if (part.endsWith("GreaterThanEquals")) {
String prop = extractProp(part, "GreaterThanEquals");
jpql.append(prop + " >= ?");
} else if (part.endsWith("Between")) {
String prop = extractProp(part, "Between");
jpql.append(prop + " < ? AND " + prop + " > ?");
} else if (part.endsWith("Like")) {
String prop = extractProp(part, "Like");
// HSQL -> LCASE, all other dbs lower
if (isHSQL()) {
jpql.append("LCASE(" + prop + ") like ?");
} else {
jpql.append("LOWER(" + prop + ") like ?");
}
} else if (part.endsWith("Ilike")) {
String prop = extractProp(part, "Ilike");
if (isHSQL()) {
jpql.append("LCASE(" + prop + ") like LCASE(?)");
} else {
jpql.append("LOWER(" + prop + ") like LOWER(?)");
}
} else if (part.endsWith("Elike")) {
String prop = extractProp(part, "Elike");
jpql.append(prop + " like ?");
} else {
String prop = extractProp(part, "");
jpql.append(prop + " = ?");
}
if (i < parts.length - 1) {
jpql.append(" AND ");
}
}
return jpql.toString();
}
private static boolean isHSQL() {
return true;
}
protected static String extractProp(String part, String end) {
String prop = part.substring(0, part.length() - end.length());
prop = (prop.charAt(0) + "").toLowerCase() + prop.substring(1);
return prop;
}
}