1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.db.wrapper;
22  
23  import java.sql.Connection;
24  import java.sql.PreparedStatement;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.sql.Statement;
28  
29  import org.efaps.db.Context;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * <p>An easy wrapper for a SQL update statement. To initialize this class use
35   * {@link org.efaps.db.databases.AbstractDatabase#newInsert(String, String, boolean)}
36   * to get a database specific insert.</p>
37   *
38   * <p><b>Example:</b><br/>
39   * <pre>
40   * SQLInsert insert = Context.getDbType().newInsert("MYTABLE", "ID", true);
41   * </pre></p>
42   *
43   * @see org.efaps.db.databases.AbstractDatabase#newInsert(String, String, boolean)
44   * @author The eFaps Team
45   * @version $Id$
46   */
47  public class SQLInsert
48      extends AbstractSQLInsertUpdate<SQLInsert>
49  {
50      /**
51       * Logging instance used in this class.
52       */
53      private static final Logger LOG = LoggerFactory.getLogger(SQLInsert.class);
54  
55      /**
56       * Must a new id created within this insert?
57       */
58      private final boolean newId;
59  
60      /**
61       * Initializes the insert. Do not call the constructor directly, instead
62       * use
63       * {@link org.efaps.db.databases.AbstractDatabase#newInsert(String, String, boolean)}
64       * to get the database specific implementation.
65       *
66       * @param _tableName    name of the table to insert
67       * @param _idCol        column holding the id
68       * @param _newId        <i>true</i> if a new id must be created; otherwise
69       *                      <i>false</i>
70       */
71      public SQLInsert(final String _tableName,
72                       final String _idCol,
73                       final boolean _newId)
74      {
75          super(_tableName, _idCol);
76          this.newId = _newId;
77      }
78  
79      /**
80       * Executes the SQL insert.
81       *
82       * @param _con  SQL connection
83       * @return if a new id must be created (defined in {@link #newId}) this new
84       *         generated id is returned
85       * @throws SQLException if insert failed
86       */
87      public Long execute(final Connection _con)
88          throws SQLException
89      {
90          final boolean supGenKey = Context.getDbType().supportsGetGeneratedKeys();
91  
92          Long ret = null;
93          if (this.newId && !supGenKey)  {
94              ret = Context.getDbType().getNewId(_con, getTableName(), getIdColumn());
95              this.column(getIdColumn(), ret);
96          }
97  
98          final StringBuilder cmd = new StringBuilder()
99              .append(Context.getDbType().getSQLPart(SQLPart.INSERT))
100             .append(" ")
101             .append(Context.getDbType().getSQLPart(SQLPart.INTO))
102             .append(" ")
103             .append(Context.getDbType().getTableQuote())
104             .append(getTableName())
105             .append(Context.getDbType().getTableQuote())
106             .append(" ")
107             .append(Context.getDbType().getSQLPart(SQLPart.PARENTHESIS_OPEN));
108 
109         final StringBuilder val = new StringBuilder();
110 
111         // append SQL values
112         boolean first = true;
113         for (final ColumnWithSQLValue col : getColumnWithSQLValues())  {
114             if (first)  {
115                 first = false;
116             } else  {
117                 cmd.append(Context.getDbType().getSQLPart(SQLPart.COMMA));
118                 val.append(Context.getDbType().getSQLPart(SQLPart.COMMA));
119             }
120             cmd.append(Context.getDbType().getColumnQuote())
121                 .append(col.getColumnName())
122                 .append(Context.getDbType().getColumnQuote());
123             val.append(col.getSqlValue());
124         }
125 
126         // append values
127         for (final AbstractColumnWithValue<?> col : getColumnWithValues())  {
128             if (first)  {
129                 first = false;
130             } else  {
131                 cmd.append(Context.getDbType().getSQLPart(SQLPart.COMMA));
132                 val.append(Context.getDbType().getSQLPart(SQLPart.COMMA));
133             }
134             cmd.append(Context.getDbType().getColumnQuote())
135                 .append(col.getColumnName())
136                 .append(Context.getDbType().getColumnQuote());
137             val.append('?');
138         }
139         cmd.append(Context.getDbType().getSQLPart(SQLPart.PARENTHESIS_CLOSE))
140             .append(Context.getDbType().getSQLPart(SQLPart.VALUES))
141             .append(Context.getDbType().getSQLPart(SQLPart.PARENTHESIS_OPEN))
142             .append(val)
143             .append(Context.getDbType().getSQLPart(SQLPart.PARENTHESIS_CLOSE));
144 
145         SQLInsert.LOG.debug("Executing SQL: {}", cmd.toString());
146 
147         PreparedStatement stmt;
148         if (this.newId && supGenKey) {
149             if (Context.getDbType().supportsMultiGeneratedKeys()) {
150                 stmt = _con.prepareStatement(cmd.toString(), new String[]{getIdColumn()});
151             } else {
152                 stmt = _con.prepareStatement(cmd.toString(), Statement.RETURN_GENERATED_KEYS);
153             }
154         } else {
155             stmt = _con.prepareStatement(cmd.toString());
156         }
157 
158         int index = 1;
159         for (final AbstractColumnWithValue<?> col : getColumnWithValues())  {
160             if (SQLInsert.LOG.isDebugEnabled()) {
161                 SQLInsert.LOG.debug("    " + index + " = " + col.getValue());
162             }
163             col.set(index++, stmt);
164         }
165 
166         try {
167             final int rows = stmt.executeUpdate();
168             if (rows == 0) {
169                 throw new SQLException("Object for SQL table '" + getTableName()
170                         + "' does not exists and was not inserted.");
171             }
172 
173             // if auto generated get new id
174             if (this.newId && supGenKey) {
175                 final ResultSet resultset = stmt.getGeneratedKeys();
176                 if (resultset.next()) {
177                     ret = resultset.getLong(1);
178                 }
179                 resultset.close();
180             }
181         } finally  {
182             stmt.close();
183         }
184 
185         if (this.newId && SQLInsert.LOG.isDebugEnabled()) {
186             SQLInsert.LOG.debug("new generated id " + ret);
187         }
188 
189         return ret;
190     }
191 }