All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bixuebihui.db.ActiveRecord Maven / Gradle / Ivy

package com.bixuebihui.db;

import com.bixuebihui.DbException;
import com.bixuebihui.jdbc.BaseDao;
import com.bixuebihui.query.Paging;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.supercsv.cellprocessor.CellProcessorAdaptor;
import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.StrReplace;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.cellprocessor.ift.StringCellProcessor;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;
import org.supercsv.util.CsvContext;

import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptySet;

/**
 * Created with IntelliJ IDEA.
 * User: Administrator
 * Date: 13-4-18
 * Time: 下午5:57
 * To change this template use File | Settings | File Templates.
 *
 * @author xingwx
 * @version $Id: $Id
 */
public interface ActiveRecord
        extends Record,
        Customizer,
        StatisticRecord {

    /**
     * Constant ORDER_ASC=0
     */
    int ORDER_ASC = 0;
    /**
     * Constant ORDER_DESC=1
     */
    int ORDER_DESC = 1;

    /**
     * the fields that query will return
     *
     * @return a set of DB fields
     */

    Set getResultFieldsSet();

    /**
     * 

in

* condition * * @param field a {@link java.lang.String} object. * @param values an array of {@link java.lang.Object} objects. * @return a {@link ActiveRecord} object. */ ActiveRecord in(String field, Object[] values); ActiveRecord in(String field, List values); ActiveRecord in(String field, SqlString sqlString); /** *

not in

* condition * * @param field a {@link java.lang.String} object. * @param values an array of {@link java.lang.Object} objects. * @return a {@link ActiveRecord} object. */ ActiveRecord notIn(String field, Object[] values); ActiveRecord notIn(String field, List values); ActiveRecord notIn(String field, SqlString sqlString); /** * Call this method will reject empty string as where condition value * * @return this */ ActiveRecord emptyStringAsNullCondition(); /** * By defautl, the rs.getInt(fieldName) return primitive type, so well turn null to zero, * if this is not desired, then call keepNullForNumberField to return null using the trick below: * ResultSet rs = new ResultSet(); *

* resultVO.setTaskId(rs.getLong("task_id")); * if(rs.wasNull()){ * resultVO.setTaskId(null); * } *

* or *

* ResultSet rs = statement.executeQuery("select Id from table"); * Integer id = 0; * if (rs.getObject("Id") != null) { * id = rs.getInt("Id"); * } else { * id = null; * } * JDBC ResultSet 的getInt, getLong, getDouble, getFloat等方法,不论数据库中是否存储为null,都返回0,或者0.0。 * 注意:这个wasNull判断一定要紧跟在你想要判断非空的那个字段取动作之后,否则它判断的就不是你所期望的那个column值了。 * * @return */ ActiveRecord keepNullForNumberField(); /** *

in.

* * @param field a {@link java.lang.String} object. * @param values a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord in(String field, Object values); /** * for union primary key search * @param fields primary key fields * @param values primary key values * @return this */ ActiveRecord in(Pair fields, List> values); /** *

like.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord like(String field, String value); /** *

startWith.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord startWith(String field, String value); /** *

eq.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord eq(String field, Object value); /** *

between.

* * @param field a {@link java.lang.String} object. * @param leftValue a {@link java.lang.Object} object. * @param rightValue a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord between(String field, Object leftValue, Object rightValue); /** *

notBetween.

* * @param field a {@link java.lang.String} object. * @param leftValue a {@link java.lang.Object} object. * @param rightValue a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord notBetween(String field, Object leftValue, Object rightValue); /** *

ne.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord ne(String field, Object value); /** *

eq.

* * @param fields an array of {@link java.lang.String} objects. * @param value an array of {@link java.lang.Object} objects. * @return a {@link ActiveRecord} object. * @throws SQLException if any. */ ActiveRecord eq(String[] fields, Object[] value) throws SQLException; /** *

greaterThan.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord greaterThan(String field, Object value); /** * greater than, same as greaterThan * @param field * @param value * @return */ ActiveRecord gt(String field, Object value); /** *

smallerThan.

* * @param field a {@link java.lang.String} object. * @param value a {@link java.lang.Object} object. * @return a {@link ActiveRecord} object. */ ActiveRecord smallerThan(String field, Object value); /** * use for native function use such as sql("json_contains(field, ?)", value) * or sql("json_contains_any(field, ?)", value), * which value is a json array such as [1,2,3], or ["x","y","z"] etc. * @param field may be a function expression * @param value value as function parameter * @return this */ ActiveRecord sql(String field, Object... value); /** * less than, same as smallerThan * @param field * @param value * @return */ ActiveRecord lt(String field, Object value); ActiveRecord isNull(String field); ActiveRecord isNotNull(String field); /** *

asc.

* * @param field a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord asc(String field); ActiveRecord and(SqlHelper andStack); ActiveRecord not(SqlHelper notStack); /** * Sort by given order * e.g. select FirstName from DemoTable where Id IN(4,5,6) order by field(Id,4,5,6); * +----+-----------+ * | Id | FirstName | * +----+-----------+ * | 1 | Chris | * | 2 | Robert | * | 3 | Mike | * | 4 | Sam | * | 5 | Carol | * | 6 | David | * +----+-----------+ * result * +-----------+ * | FirstName | * +-----------+ * | Sam | * | Carol | * | David | * +-----------+ * * @param field field name * @param in fields value in IN clause * @return this */ ActiveRecord orderByIn(String field, Object[] in); /** *

desc.

* * @param field a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord desc(String field); /** *

limit.

* * @param begin a int. * @param num a int. * @return a {@link ActiveRecord} object. */ ActiveRecord limit(int begin, int num); /** *

last.

* * @return a {@link Record} object. */ Record last(); /** * @param field db field * @param value value to compare * @return this */ ActiveRecord greaterOrEqualThan(String field, Object value); /** * greater than or equal, same as greaterOrEqualThan * @param field * @param value * @return */ ActiveRecord ge(String field, Object value); /** * @param field db field * @param value value to compare * @return this */ ActiveRecord smallerOrEqualThan(String field, Object value); /** * less than or equal, same as smallerOrEqualThan * @param field * @param value * @return */ ActiveRecord le(String field, Object value); /** * @param field db field * @param value value to compare */ ActiveRecord wildcard(String field, String[] value); /** *

getCondStack.

* * @return a {@link SqlHelper} object. */ SqlHelper getCondStack(); /** *

or.

* * @param andStack a {@link SqlHelper} object. * @return a {@link ActiveRecord} object. */ ActiveRecord or(SqlHelper andStack); default ActiveRecord or(ActiveRecord orStack){ return or(orStack.getCondStack()); } default ActiveRecord and(ActiveRecord andStack){ return and(andStack.getCondStack()); } default ActiveRecord not(ActiveRecord notStack){ return not(notStack.getCondStack()); } /** *

alias.

* * @param shortName a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord alias(String shortName); /** * Override table name for partition or other porpoise, otherwise you can omit to call this * * @param partitionTableName * @return */ ActiveRecord tableName(String partitionTableName); /** * return ResultSet fields * @param fields table or view fields * @return this */ ActiveRecord fields(String... fields); /** *

join.

* * @param joinClause a {@link java.lang.String} object. * @return a {@link ActiveRecord} object. */ ActiveRecord join(String joinClause); /** * sql group by clause * * @param fields * @return */ ActiveRecord groupBy(String... fields); ActiveRecord having(HavingConditon fieldProcessor); static Set toFieldsSet(String commaSeparatedFields) { if (StringUtils.isBlank(commaSeparatedFields) || commaSeparatedFields.contains("*")) { return emptySet(); } return Stream.of(commaSeparatedFields.split(",")).map(x -> BaseDao.getFieldAlias(x.toLowerCase(Locale.ROOT))).collect(Collectors.toSet()); } /** * use 'search bean' value to query result: * when search bean's property is primitive, this is equals `eq`, * when property is String, this is like 'like', * when property is Date/Time, this is between operation * * @param searchBean * @param fields valid table/view fields * @param * @return */ default ActiveRecord query(Paging searchBean, Set fields) { return searchBean.toActiveRecordParams(this, fields); } ActiveRecord query(Paging searchBean); default String findAllToCsv(String titles) throws DbException { return findAllToCsv(titles, null); } /** * export to CSV format with Excel preferences * @param titles i18n titles * @param onlyFields field names of POJO type T * @return csv string * @throws DbException if any. */ default String findAllToCsv(String titles, String onlyFields) throws DbException { return findAllToCsv(titles, onlyFields, false, false); } default String findAllToCsv(String titles, String onlyFields, boolean withBom, boolean forceStringForDateTime) throws DbException { List list = findAll(); return findAllToCsv(list, titles, onlyFields, withBom, forceStringForDateTime, null); } default String findAllToCsv(String titles, String onlyFields, Map customFormatters) throws DbException { List list = findAll(); return findAllToCsv(list, titles, onlyFields, true, true, customFormatters); } ActiveRecord coreSqlInterceptor(Function coreSqlInterceptor); class MyCustomCellProcessor extends CellProcessorAdaptor { // 匹配日期时间格式,如 2020-01-01 12:00:00.000 或 2020-01-01 12:00:00 // 或长数字 20200101120000001 static String regex = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$|^\\d{11,20}$"; static String replacement = "=\"$0\""; public MyCustomCellProcessor() { super(new Optional(new StrReplace(regex, replacement))); } public MyCustomCellProcessor(StringCellProcessor next) { super(new Optional(new StrReplace(regex, replacement, next))); } @Override public T execute(Object value, CsvContext context) { return next.execute(value, context); } } static String findAllToCsv(List list, String titles, String onlyFields) throws DbException { return findAllToCsv(list, titles, onlyFields, false, false, null); } static String findAllToCsv(List list, String titles, String onlyFields, Map customFormatters) throws DbException { return findAllToCsv(list, titles, onlyFields, false, false, customFormatters); } static String findAllToCsv(List list, String titles, String onlyFields, boolean withBom, boolean forceStringForDateTime, Map customFormatters) throws DbException { if(list.isEmpty()) return ""; String[] fields = onlyFields!=null ? onlyFields.split(",") : Arrays.stream(list.get(0).getClass().getDeclaredFields()) .map(Field::getName).filter(name -> !name.contains("$")).toArray(String[]::new); StringWriter sb = new StringWriter(10_000); // add bom header to StringBuilder if(withBom) { sb.write("\uFEFF"); } ICsvBeanWriter beanWriter = new CsvBeanWriter(sb, CsvPreference.EXCEL_PREFERENCE); String[] header = titles.split(","); try { beanWriter.writeHeader(header); if(forceStringForDateTime) { CellProcessor[] processors = new CellProcessor[fields.length]; for (int i = 0; i < fields.length; i++) { if(customFormatters!=null && customFormatters.containsKey(fields[i])) { processors[i] = customFormatters.get(fields[i]); }else { processors[i] = new MyCustomCellProcessor(); } } for (T pojo : list) { beanWriter.write(pojo, fields, processors); } }else{ CellProcessor[] processors = new CellProcessor[fields.length]; for (int i = 0; i < fields.length; i++) { if(customFormatters!=null && customFormatters.containsKey(fields[i])) { processors[i] = customFormatters.get(fields[i]); } } for (T pojo : list) { beanWriter.write(pojo, fields, processors); } } beanWriter.close(); } catch (IOException e) { throw new DbException(e); } return sb.toString(); } }