/* * Copyright 2008 Pavel Syrtsov * * Licensed 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 com.sf.ddao.factory; import com.sf.ddao.conn.ConnectionHandlerHelper; import com.sf.ddao.factory.param.ParameterException; import com.sf.ddao.factory.param.ParameterFactory; import com.sf.ddao.factory.param.ParameterHandler; import org.apache.commons.chain.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.lang.reflect.AnnotatedElement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * DefaultStatementFactory creates new prepared statment using simplified IBATIS like query syntax: <br/> * value reference can be put using number that reflect 0 based method argument number * that will be used as simle value * (and as such can not be java bean or any other non trivial object) * or it can be name of java bean property or Map object that is 1st argument in the * list of method arguments. * Query paramter can be added as inline value by enclosing it in '$' or * it can be added as bound value by enclosing it in '#'. Inline value will be injected in * query text before query will be passed to JDBC connection. Bound value will be bound to * prepared statement before it's execution. * <p/> * Created by Pavel Syrtsov * Date: Nov 3, 2007 * Time: 9:24:44 PM */ @SuppressWarnings("UnusedDeclaration") public class DefaultStatementFactory implements StatementFactory { public static final Logger log = LoggerFactory.getLogger(DefaultStatementFactory.class.getName()); private List<ParameterHandler> inlineParametersList = new ArrayList<ParameterHandler>(); private List<ParameterHandler> refParametersList = new ArrayList<ParameterHandler>(); private List<String> stmtTokens = new ArrayList<String>(); @Inject private ParameterFactory parameterFactory; public void init(AnnotatedElement element, String sql) throws StatementFactoryException { try { char lastChar = 0; boolean paramStarted = false; StringBuilder token = new StringBuilder(sql.length()); StringBuilder param = new StringBuilder(); for (char ch : sql.toCharArray()) { if (paramStarted) { if (lastChar == ch) { stmtTokens.add(token.toString()); token.delete(0, token.length()); if (ch == '$') { addInlineParameter(element, param.toString()); } else { addRefParameter(element, param.toString()); } param.delete(0, param.length()); paramStarted = false; continue; } param.append(ch); continue; } if (ch == '#' || ch == '$') { paramStarted = true; lastChar = ch; continue; } token.append(ch); } stmtTokens.add(token.toString()); } catch (Exception e) { throw new StatementFactoryException("Failed to extract statement parameters for " + element, e); } } public void addRefParameter(AnnotatedElement element, String name) throws ParameterException { ParameterHandler parameter = parameterFactory.createStatementParameter(element, name, true); refParametersList.add(parameter); inlineParametersList.add(parameter); } public void addInlineParameter(AnnotatedElement element, String name) throws ParameterException { ParameterHandler parameter = parameterFactory.createStatementParameter(element, name, false); inlineParametersList.add(parameter); } public PreparedStatement createStatement(Context context, boolean returnGeneratedKeys) throws StatementFactoryException { String stmt = null; try { stmt = createText(context); log.debug("Created statement:{}, applying parameters: {}", stmt, refParametersList); final Connection connection = ConnectionHandlerHelper.getConnection(context); PreparedStatement preparedStatement; if (returnGeneratedKeys) { preparedStatement = connection.prepareStatement(stmt, Statement.RETURN_GENERATED_KEYS); } else { preparedStatement = connection.prepareStatement(stmt); } int i = 1; for (ParameterHandler parameter : refParametersList) { i += parameter.bindParam(preparedStatement, i, context); } return preparedStatement; } catch (Exception e) { if (stmt == null) { stmt = stmtTokens.toString(); } throw new StatementFactoryException("Failed to prepare statement '" + stmt + "'", e); } } public String createText(Context context) throws SQLException { Iterator<String> iterator = stmtTokens.iterator(); String stmt = iterator.next(); if (iterator.hasNext()) { StringBuilder sb = new StringBuilder(); sb.append(stmt); for (ParameterHandler parameter : inlineParametersList) { parameter.appendParam(context, sb); sb.append(iterator.next()); } stmt = sb.toString(); } return stmt; } public List<ParameterHandler> getInlineParametersList() { return inlineParametersList; } public List<ParameterHandler> getRefParametersList() { return refParametersList; } public List<String> getStmtTokens() { return stmtTokens; } public ParameterFactory getParameterFactory() { return parameterFactory; } }