/** * Copyright 2015 Santhosh Kumar Tekuri * * The JLibs authors license this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package jlibs.jdbc; import jlibs.core.util.CollectionUtil; import java.lang.reflect.InvocationTargetException; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * @author Santhosh Kumar T */ public abstract class DAO<T> implements RowMapper<T>{ public final JDBC jdbc; public final TableMetaData table; public DAO(JDBC jdbc, TableMetaData table){ this.jdbc = jdbc; this.table = table; buildQueries(); } @SuppressWarnings({"unchecked"}) public static <T> DAO<T> create(Class<T> clazz, JDBC jdbc){ try{ String qname = "${package}._${class}DAO".replace("${package}", clazz.getPackage()!=null?clazz.getPackage().getName():"") .replace("${class}", clazz.getSimpleName()); if(qname.startsWith(".")) // default package qname = qname.substring(1); Class tableClass = clazz.getClassLoader().loadClass(qname); return (DAO<T>)tableClass.getConstructor(JDBC.class).newInstance(jdbc); }catch(ClassNotFoundException ex){ throw new RuntimeException(ex); } catch(InstantiationException ex){ throw new RuntimeException(ex); } catch(IllegalAccessException ex){ throw new RuntimeException(ex); } catch(NoSuchMethodException e){ throw new RuntimeException(e); } catch(InvocationTargetException e){ throw new RuntimeException(e); } } /*-------------------------------------------------[ Abstract Methods ]---------------------------------------------------*/ public abstract T newRow(); public abstract Object getColumnValue(int i, T record); public abstract void setColumnValue(int i, T record, Object value); public abstract T newRecord(ResultSet rs) throws SQLException; public abstract Object getAutoColumnValue(ResultSet rs) throws SQLException; /*-------------------------------------------------[ Helpers ]---------------------------------------------------*/ private Object[] values(T record, int order[]){ Object args[] = new Object[order.length]; for(int i=0; i<args.length; i++) args[i] = getColumnValue(order[i], record); return args; } /*-------------------------------------------------[ Queries ]---------------------------------------------------*/ private String selectQuery; private String insertQuery; private int insertOrder[]; private String updateQuery; private int updateOrder[]; private String deleteQuery; private int deleteOrder[]; private void buildQueries(){ // SELECT Query StringBuilder query = new StringBuilder("SELECT "); for(int i=0; i<table.columns.length; i++){ if(i>0) query.append(','); query.append(table.columns[i].name); } query.append(" FROM ").append(table.name).append(" "); selectQuery = query.toString(); // INSERT Query query.setLength(0); query.append('('); boolean first = true; for(ColumnMetaData column: table.columns){ if(!column.auto){ if(first) first = false; else query.append(','); query.append(column.name); } } query.append(") VALUES("); first = true; List<Integer> args = new ArrayList<Integer>(); for(int i=0; i<table.columns.length; i++){ if(!table.columns[i].auto){ if(first) first = false; else query.append(','); query.append('?'); args.add(i); } } query.append(')'); insertQuery = query.toString(); if(table.autoColumn!=-1) insertOrder = CollectionUtil.toIntArray(args); // UPDATE Query query.setLength(0); args.clear(); for(int i=0; i<table.columns.length; i++){ if(!table.columns[i].primary){ if(args.size()==0) query.append(" SET "); else query.append(", "); query.append(table.columns[i].name).append("=?"); args.add(i); } } int size = args.size(); for(int i=0; i<table.columns.length; i++){ if(table.columns[i].primary){ if(args.size()>size) query.append(" AND "); else query.append(" WHERE "); query.append(table.columns[i].name).append("=?"); args.add(i); } } updateQuery = query.toString(); updateOrder = CollectionUtil.toIntArray(args); // DELETE Query query.setLength(0); args.clear(); for(int i=0; i<table.columns.length; i++){ if(table.columns[i].primary){ if(args.size()==0) query.append(" WHERE "); else query.append(" AND "); query.append(table.columns[i].name).append("=?"); args.add(i); } } deleteQuery = query.toString(); deleteOrder = CollectionUtil.toIntArray(args); } /*-------------------------------------------------[ Select ]---------------------------------------------------*/ private String selectQuery(String condition){ return condition==null ? selectQuery : selectQuery+condition; } public List<T> all() throws DAOException{ return all(null); } public List<T> all(String condition, Object... args) throws DAOException{ return top(0, condition, args); } public List<T> top(int max, String condition, Object... args) throws DAOException{ return jdbc.selectTop(max, selectQuery(condition), this, args); } public T first() throws DAOException{ return first(null); } public T first(String condition, Object... args) throws DAOException{ return jdbc.selectFirst(selectQuery(condition), this, args); } /*-------------------------------------------------[ Count ]---------------------------------------------------*/ protected int integer(String functionCall, String condition, Object... args) throws DAOException{ if(condition==null) condition = ""; return jdbc.selectFirst("SELECT "+functionCall+" FROM "+table.name+' '+condition, new RowMapper<Integer>(){ @Override public Integer newRecord(ResultSet rs) throws SQLException{ return rs.getInt(1); } }, args); } public int count(String condition, Object... args) throws DAOException{ return integer("count(*)", condition, args); } /*-------------------------------------------------[ Insert ]---------------------------------------------------*/ private final RowMapper<Object> generaedKeyMapper = new RowMapper<Object>(){ @Override public Object newRecord(ResultSet rs) throws SQLException{ return getAutoColumnValue(rs); } }; public Object insert(String query, Object... args) throws DAOException{ if(query==null) query = ""; if(table.autoColumn==-1){ jdbc.executeUpdate("INSERT INTO "+table.name+" "+query, args); return null; }else return jdbc.executeUpdate("INSERT INTO "+table.name+" "+query, generaedKeyMapper, args); } public void insert(T record) throws DAOException{ if(table.autoColumn==-1){ Object args[] = new Object[table.columns.length]; for(int i=0; i<table.columns.length; i++) args[i] = getColumnValue(i, record); insert(insertQuery, args); }else{ Object generatedKey = insert(insertQuery, values(record, insertOrder)); setColumnValue(table.autoColumn, record, generatedKey); } } /*-------------------------------------------------[ Update ]---------------------------------------------------*/ public int update(String query, Object... args) throws DAOException{ if(query==null) query = ""; return jdbc.executeUpdate("UPDATE "+table.name+" "+query, args); } public int update(T record) throws DAOException{ return update(updateQuery, values(record, updateOrder)); } /*-------------------------------------------------[ Upsert ]---------------------------------------------------*/ public void upsert(T record) throws DAOException{ if(update(record)==0) insert(record); } /*-------------------------------------------------[ Delete ]---------------------------------------------------*/ public int delete(String query, Object... args) throws DAOException{ if(query==null) query = ""; return jdbc.executeUpdate("DELETE FROM "+table.name+" "+query, args); } public int delete() throws DAOException{ return delete(null, new Object[0]); } public boolean delete(T record) throws DAOException{ return delete(deleteQuery, values(record, deleteOrder))==1; } }