/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses 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 com.cloudera.sqoop.orm; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.Statement; import java.sql.SQLException; import java.util.Random; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.util.Shell; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import com.cloudera.sqoop.SqoopOptions; import com.cloudera.sqoop.TestConnFactory.DummyManager; import com.cloudera.sqoop.manager.ConnManager; import com.cloudera.sqoop.testutil.DirUtil; import com.cloudera.sqoop.testutil.HsqldbTestServer; import com.cloudera.sqoop.testutil.ImportJobTestCase; import com.cloudera.sqoop.tool.ImportTool; import com.cloudera.sqoop.util.ClassLoaderStack; import org.junit.rules.ExpectedException; import java.lang.reflect.Field; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Test that the ClassWriter generates Java classes based on the given table, * which compile. */ public class TestClassWriter { public static final Log LOG = LogFactory.getLog(TestClassWriter.class.getName()); private static final String WIDE_TABLE_NAME = "WIDETABLE"; private static final int WIDE_TABLE_COLUMN_COUNT = 800; private static final int WIDE_TABLE_ROW_COUNT = 20_000; // instance variables populated during setUp, used during tests private HsqldbTestServer testServer; private ConnManager manager; private SqoopOptions options; @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void setUp() { testServer = new HsqldbTestServer(); org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger(); root.setLevel(org.apache.log4j.Level.DEBUG); try { testServer.resetServer(); } catch (SQLException sqlE) { LOG.error("Got SQLException: " + sqlE.toString()); fail("Got SQLException: " + sqlE.toString()); } catch (ClassNotFoundException cnfe) { LOG.error("Could not find class for db driver: " + cnfe.toString()); fail("Could not find class for db driver: " + cnfe.toString()); } manager = testServer.getManager(); options = testServer.getSqoopOptions(); // sanity check: make sure we're in a tmp dir before we blow anything away. assertTrue("Test generates code in non-tmp dir!", CODE_GEN_DIR.startsWith(ImportJobTestCase.TEMP_BASE_DIR)); assertTrue("Test generates jars in non-tmp dir!", JAR_GEN_DIR.startsWith(ImportJobTestCase.TEMP_BASE_DIR)); // start out by removing these directories ahead of time // to ensure that this is truly generating the code. File codeGenDirFile = new File(CODE_GEN_DIR); File classGenDirFile = new File(JAR_GEN_DIR); if (codeGenDirFile.exists()) { LOG.debug("Removing code gen dir: " + codeGenDirFile); if (!DirUtil.deleteDir(codeGenDirFile)) { LOG.warn("Could not delete " + codeGenDirFile + " prior to test"); } } if (classGenDirFile.exists()) { LOG.debug("Removing class gen dir: " + classGenDirFile); if (!DirUtil.deleteDir(classGenDirFile)) { LOG.warn("Could not delete " + classGenDirFile + " prior to test"); } } } @After public void tearDown() { try { manager.close(); } catch (SQLException sqlE) { LOG.error("Got SQLException: " + sqlE.toString()); fail("Got SQLException: " + sqlE.toString()); } } static final String CODE_GEN_DIR = ImportJobTestCase.TEMP_BASE_DIR + "sqoop/test/codegen"; static final String JAR_GEN_DIR = ImportJobTestCase.TEMP_BASE_DIR + "sqoop/test/jargen"; private File runGenerationTest(String[] argv, String classNameToCheck) { return runGenerationTest(argv, classNameToCheck, HsqldbTestServer.getTableName()); } /** * Run a test to verify that we can generate code and it emits the output * files where we expect them. * @return */ private File runGenerationTest(String[] argv, String classNameToCheck, String tableName) { File codeGenDirFile = new File(CODE_GEN_DIR); File classGenDirFile = new File(JAR_GEN_DIR); try { options = new ImportTool().parseArguments(argv, null, options, true); } catch (Exception e) { LOG.error("Could not parse options: " + e.toString()); } CompilationManager compileMgr = new CompilationManager(options); ClassWriter writer = new ClassWriter(options, manager, tableName, compileMgr); try { writer.generate(); compileMgr.compile(); compileMgr.jar(); } catch (IOException ioe) { LOG.error("Got IOException: " + ioe.toString()); fail("Got IOException: " + ioe.toString()); } String classFileNameToCheck = classNameToCheck.replace('.', File.separatorChar); LOG.debug("Class file to check for: " + classFileNameToCheck); // Check that all the files we expected to generate (.java, .class, .jar) // exist. File tableFile = new File(codeGenDirFile, classFileNameToCheck + ".java"); assertTrue("Cannot find generated source file for table!", tableFile.exists()); LOG.debug("Found generated source: " + tableFile); File tableClassFile = new File(classGenDirFile, classFileNameToCheck + ".class"); assertTrue("Cannot find generated class file for table!", tableClassFile.exists()); LOG.debug("Found generated class: " + tableClassFile); File jarFile = new File(compileMgr.getJarFilename()); assertTrue("Cannot find compiled jar", jarFile.exists()); LOG.debug("Found generated jar: " + jarFile); // check that the .class file made it into the .jar by enumerating // available entries in the jar file. boolean foundCompiledClass = false; if (Shell.WINDOWS) { // In Windows OS, elements in jar files still need to have a path // separator of '/' rather than the default File.separator which is '\' classFileNameToCheck = classFileNameToCheck.replace(File.separator, "/"); } try { JarInputStream jis = new JarInputStream(new FileInputStream(jarFile)); LOG.debug("Jar file has entries:"); while (true) { JarEntry entry = jis.getNextJarEntry(); if (null == entry) { // no more entries. break; } if (entry.getName().equals(classFileNameToCheck + ".class")) { foundCompiledClass = true; LOG.debug(" * " + entry.getName()); } else { LOG.debug(" " + entry.getName()); } } jis.close(); } catch (IOException ioe) { fail("Got IOException iterating over Jar file: " + ioe.toString()); } assertTrue("Cannot find .class file " + classFileNameToCheck + ".class in jar file", foundCompiledClass); LOG.debug("Found class in jar - test success!"); return jarFile; } /** * Test that we can generate code. Test that we can redirect the --outdir * and --bindir too. */ @Test public void testCodeGen() { // Set the option strings in an "argv" to redirect our srcdir and bindir. String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, }; runGenerationTest(argv, HsqldbTestServer.getTableName()); } private static final String OVERRIDE_CLASS_NAME = "override"; /** * Test that we can generate code with a custom class name. */ @Test public void testSetClassName() { // Set the option strings in an "argv" to redirect our srcdir and bindir String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--class-name", OVERRIDE_CLASS_NAME, }; runGenerationTest(argv, OVERRIDE_CLASS_NAME); } private static final String OVERRIDE_CLASS_AND_PACKAGE_NAME = "override.pkg.prefix.classname"; /** * Test that we can generate code with a custom class name that includes a * package. */ @Test public void testSetClassAndPackageName() { // Set the option strings in an "argv" to redirect our srcdir and bindir String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--class-name", OVERRIDE_CLASS_AND_PACKAGE_NAME, }; runGenerationTest(argv, OVERRIDE_CLASS_AND_PACKAGE_NAME); } private static final String OVERRIDE_PACKAGE_NAME = "special.userpackage.name"; /** * Test that we can generate code with a custom class name that includes a * package. */ @Test public void testSetPackageName() { // Set the option strings in an "argv" to redirect our srcdir and bindir String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--package-name", OVERRIDE_PACKAGE_NAME, }; runGenerationTest(argv, OVERRIDE_PACKAGE_NAME + "." + HsqldbTestServer.getTableName()); } // Test the SQL identifier -> Java identifier conversion. @Test public void testJavaIdentifierConversion() { assertNull(ClassWriter.getIdentifierStrForChar(' ')); assertNull(ClassWriter.getIdentifierStrForChar('\t')); assertNull(ClassWriter.getIdentifierStrForChar('\r')); assertNull(ClassWriter.getIdentifierStrForChar('\n')); assertEquals("x", ClassWriter.getIdentifierStrForChar('x')); assertEquals("_", ClassWriter.getIdentifierStrForChar('-')); assertEquals("_", ClassWriter.getIdentifierStrForChar('_')); assertEquals("foo", ClassWriter.toJavaIdentifier("foo")); assertEquals("_abstract", ClassWriter.toJavaIdentifier("abstract")); assertEquals("_assert", ClassWriter.toJavaIdentifier("assert")); assertEquals("_boolean", ClassWriter.toJavaIdentifier("boolean")); assertEquals("_break", ClassWriter.toJavaIdentifier("break")); assertEquals("_byte", ClassWriter.toJavaIdentifier("byte")); assertEquals("_case", ClassWriter.toJavaIdentifier("case")); assertEquals("_catch", ClassWriter.toJavaIdentifier("catch")); assertEquals("_char", ClassWriter.toJavaIdentifier("char")); assertEquals("_class", ClassWriter.toJavaIdentifier("class")); assertEquals("_const", ClassWriter.toJavaIdentifier("const")); assertEquals("_continue", ClassWriter.toJavaIdentifier("continue")); assertEquals("_default", ClassWriter.toJavaIdentifier("default")); assertEquals("_do", ClassWriter.toJavaIdentifier("do")); assertEquals("_double", ClassWriter.toJavaIdentifier("double")); assertEquals("_else", ClassWriter.toJavaIdentifier("else")); assertEquals("_enum", ClassWriter.toJavaIdentifier("enum")); assertEquals("_extends", ClassWriter.toJavaIdentifier("extends")); assertEquals("_false", ClassWriter.toJavaIdentifier("false")); assertEquals("_final", ClassWriter.toJavaIdentifier("final")); assertEquals("_finally", ClassWriter.toJavaIdentifier("finally")); assertEquals("_float", ClassWriter.toJavaIdentifier("float")); assertEquals("_for", ClassWriter.toJavaIdentifier("for")); assertEquals("_goto", ClassWriter.toJavaIdentifier("goto")); assertEquals("_if", ClassWriter.toJavaIdentifier("if")); assertEquals("_implements", ClassWriter.toJavaIdentifier("implements")); assertEquals("_import", ClassWriter.toJavaIdentifier("import")); assertEquals("_instanceof", ClassWriter.toJavaIdentifier("instanceof")); assertEquals("_int", ClassWriter.toJavaIdentifier("int")); assertEquals("_interface", ClassWriter.toJavaIdentifier("interface")); assertEquals("_long", ClassWriter.toJavaIdentifier("long")); assertEquals("_native", ClassWriter.toJavaIdentifier("native")); assertEquals("_new", ClassWriter.toJavaIdentifier("new")); assertEquals("_null", ClassWriter.toJavaIdentifier("null")); assertEquals("_package", ClassWriter.toJavaIdentifier("package")); assertEquals("_private", ClassWriter.toJavaIdentifier("private")); assertEquals("_protected", ClassWriter.toJavaIdentifier("protected")); assertEquals("_public", ClassWriter.toJavaIdentifier("public")); assertEquals("_return", ClassWriter.toJavaIdentifier("return")); assertEquals("_short", ClassWriter.toJavaIdentifier("short")); assertEquals("_static", ClassWriter.toJavaIdentifier("static")); assertEquals("_strictfp", ClassWriter.toJavaIdentifier("strictfp")); assertEquals("_super", ClassWriter.toJavaIdentifier("super")); assertEquals("_switch", ClassWriter.toJavaIdentifier("switch")); assertEquals("_synchronized", ClassWriter.toJavaIdentifier("synchronized")); assertEquals("_this", ClassWriter.toJavaIdentifier("this")); assertEquals("_throw", ClassWriter.toJavaIdentifier("throw")); assertEquals("_throws", ClassWriter.toJavaIdentifier("throws")); assertEquals("_transient", ClassWriter.toJavaIdentifier("transient")); assertEquals("_true", ClassWriter.toJavaIdentifier("true")); assertEquals("_try", ClassWriter.toJavaIdentifier("try")); assertEquals("_void", ClassWriter.toJavaIdentifier("void")); assertEquals("_volatile", ClassWriter.toJavaIdentifier("volatile")); assertEquals("_while", ClassWriter.toJavaIdentifier("while")); assertEquals("_class", ClassWriter.toJavaIdentifier("cla ss")); assertEquals("_int", ClassWriter.toJavaIdentifier("int")); assertEquals("thisismanywords", ClassWriter.toJavaIdentifier( "this is many words")); assertEquals("_9isLegalInSql", ClassWriter.toJavaIdentifier( "9isLegalInSql")); assertEquals("____", ClassWriter.toJavaIdentifier("___")); assertEquals("__class", ClassWriter.toJavaIdentifier("_class")); //Checking Java identifier for Constant PROTOCOL_VERSION assertEquals("_PROTOCOL_VERSION", ClassWriter.toJavaIdentifier("PROTOCOL_VERSION")); } @Test public void testWeirdColumnNames() throws SQLException { // Recreate the table with column names that aren't legal Java identifiers. String tableName = HsqldbTestServer.getTableName(); Connection connection = testServer.getConnection(); Statement st = connection.createStatement(); try { st.executeUpdate("DROP TABLE " + tableName + " IF EXISTS"); st.executeUpdate("CREATE TABLE " + tableName + " (class INT, \"9field\" INT)"); st.executeUpdate("INSERT INTO " + tableName + " VALUES(42, 41)"); connection.commit(); } finally { st.close(); connection.close(); } String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--package-name", OVERRIDE_PACKAGE_NAME, }; runGenerationTest(argv, OVERRIDE_PACKAGE_NAME + "." + HsqldbTestServer.getTableName()); } // Test For checking Codegneration perfroming successfully // in case of Table with Column name as PROTOCOL_VERSION @Test public void testColumnNameAsProtocolVersion() throws SQLException { // Recreate the table with column name as PROTOCOL_VERSION. String tableName = HsqldbTestServer.getTableName(); Connection connection = testServer.getConnection(); Statement st = connection.createStatement(); try { st.executeUpdate("DROP TABLE " + tableName + " IF EXISTS"); st.executeUpdate("CREATE TABLE " + tableName + " (PROTOCOL_VERSION INT)"); st.executeUpdate("INSERT INTO " + tableName + " VALUES(42)"); connection.commit(); } finally { st.close(); connection.close(); } String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--package-name", OVERRIDE_PACKAGE_NAME, }; runGenerationTest(argv, OVERRIDE_PACKAGE_NAME + "." + HsqldbTestServer.getTableName()); } @Test public void testCloningTableWithVarbinaryDoesNotThrowNPE() throws SQLException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String tableName = HsqldbTestServer.getTableName(); Connection connection = testServer.getConnection(); Statement st = connection.createStatement(); try { st.executeUpdate("DROP TABLE " + tableName + " IF EXISTS"); st.executeUpdate("CREATE TABLE " + tableName + " (id INT, test VARBINARY(10))"); connection.commit(); } finally { st.close(); connection.close(); } String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--package-name", OVERRIDE_PACKAGE_NAME, }; String className = OVERRIDE_PACKAGE_NAME + "." + HsqldbTestServer.getTableName(); File ormJarFile = runGenerationTest(argv, className); ClassLoader prevClassLoader = ClassLoaderStack.addJarFile( ormJarFile.getCanonicalPath(), className); Class tableClass = Class.forName(className, true, Thread.currentThread().getContextClassLoader()); Method cloneImplementation = tableClass.getMethod("clone"); Object instance = tableClass.newInstance(); assertTrue(cloneImplementation.invoke(instance).getClass(). getCanonicalName().equals(className)); if (null != prevClassLoader) { ClassLoaderStack.setCurrentClassLoader(prevClassLoader); } } /** * Test the generated equals method. * @throws IOException * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalArgumentException */ @Test public void testEqualsMethod() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { // Set the option strings in an "argv" to redirect our srcdir and bindir String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--class-name", OVERRIDE_CLASS_AND_PACKAGE_NAME, }; File ormJarFile = runGenerationTest(argv, OVERRIDE_CLASS_AND_PACKAGE_NAME); ClassLoader prevClassLoader = ClassLoaderStack.addJarFile( ormJarFile.getCanonicalPath(), OVERRIDE_CLASS_AND_PACKAGE_NAME); Class tableClass = Class.forName( OVERRIDE_CLASS_AND_PACKAGE_NAME, true, Thread.currentThread().getContextClassLoader()); Method setterIntField1 = tableClass.getMethod("set_INTFIELD1", Integer.class); Method setterIntField2 = tableClass.getMethod("set_INTFIELD2", Integer.class); Method equalsImplementation = tableClass.getMethod("equals", Object.class); Object instance1 = tableClass.newInstance(); Object instance2 = tableClass.newInstance(); // test reflexivity assertTrue((Boolean) equalsImplementation.invoke(instance1, instance1)); // test equality for uninitialized fields assertTrue((Boolean) equalsImplementation.invoke(instance1, instance2)); // test symmetry assertTrue((Boolean) equalsImplementation.invoke(instance2, instance1)); // test reflexivity with initialized fields setterIntField1.invoke(instance1, new Integer(1)); setterIntField2.invoke(instance1, new Integer(2)); assertTrue((Boolean) equalsImplementation.invoke(instance1, instance1)); // test difference in both fields setterIntField1.invoke(instance2, new Integer(3)); setterIntField2.invoke(instance2, new Integer(4)); assertFalse((Boolean) equalsImplementation.invoke(instance1, instance2)); // test difference in second field setterIntField1.invoke(instance2, new Integer(1)); setterIntField2.invoke(instance2, new Integer(3)); assertFalse((Boolean) equalsImplementation.invoke(instance1, instance2)); // test difference in first field setterIntField1.invoke(instance2, new Integer(3)); setterIntField2.invoke(instance2, new Integer(2)); assertFalse((Boolean) equalsImplementation.invoke(instance1, instance2)); // test equality for initialized fields setterIntField1.invoke(instance2, new Integer(1)); setterIntField2.invoke(instance2, new Integer(2)); assertTrue((Boolean) equalsImplementation.invoke(instance1, instance2)); if (null != prevClassLoader) { ClassLoaderStack.setCurrentClassLoader(prevClassLoader); } } private static final String USERMAPPING_CLASS_AND_PACKAGE_NAME = "usermapping.pkg.prefix.classname"; @Test public void testUserMapping() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { // Set the option strings in an "argv" to redirect our srcdir and bindir String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--class-name", USERMAPPING_CLASS_AND_PACKAGE_NAME, "--map-column-java", "INTFIELD1=String", }; File ormJarFile = runGenerationTest(argv, USERMAPPING_CLASS_AND_PACKAGE_NAME); ClassLoader prevClassLoader = ClassLoaderStack.addJarFile( ormJarFile.getCanonicalPath(), USERMAPPING_CLASS_AND_PACKAGE_NAME); Class tableClass = Class.forName( USERMAPPING_CLASS_AND_PACKAGE_NAME, true, Thread.currentThread().getContextClassLoader()); try { Field intfield = tableClass.getDeclaredField("INTFIELD1"); assertEquals(String.class, intfield.getType()); } catch (NoSuchFieldException ex) { fail("Can't find field for INTFIELD1"); } catch (SecurityException ex) { fail("Can't find field for INTFIELD1"); } if (null != prevClassLoader) { ClassLoaderStack.setCurrentClassLoader(prevClassLoader); } } @Test public void testBrokenUserMapping() throws Exception { String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, "--class-name", USERMAPPING_CLASS_AND_PACKAGE_NAME, "--map-column-java", "INTFIELD1=NotARealClass", }; try { runGenerationTest( argv, USERMAPPING_CLASS_AND_PACKAGE_NAME); } catch(IllegalArgumentException e) { return; } fail("we shouldn't successfully generate code"); } /** * A dummy manager that declares that it ORM is self managed. */ public static class DummyDirectManager extends DummyManager { @Override public boolean isORMFacilitySelfManaged() { return true; } } @Test public void testNoClassGeneration() throws Exception { manager = new DummyDirectManager(); String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, }; try { options = new ImportTool().parseArguments(argv, null, options, true); } catch (Exception e) { LOG.error("Could not parse options: " + e.toString()); } CompilationManager compileMgr = new CompilationManager(options); ClassWriter writer = new ClassWriter(options, manager, HsqldbTestServer.getTableName(), compileMgr); writer.generate(); thrown.expect(Exception.class); compileMgr.compile(); } @Test(timeout = 25000) public void testWideTableClassGeneration() throws Exception { createWideTable(); options = new SqoopOptions(HsqldbTestServer.getDbUrl(), WIDE_TABLE_NAME); // Set the option strings in an "argv" to redirect our srcdir and bindir. String [] argv = { "--bindir", JAR_GEN_DIR, "--outdir", CODE_GEN_DIR, }; File ormJarFile = runGenerationTest(argv, WIDE_TABLE_NAME, WIDE_TABLE_NAME); ClassLoader prevClassLoader = ClassLoaderStack.addJarFile(ormJarFile.getCanonicalPath(), WIDE_TABLE_NAME); Class tableClass = Class.forName(WIDE_TABLE_NAME, true, Thread.currentThread().getContextClassLoader()); Object instance = tableClass.newInstance(); Method setterMethod = tableClass.getMethod("setField", String.class, Object.class); Random random = new Random(0); for (int j = 0; j < WIDE_TABLE_ROW_COUNT; ++j) { for (int i = 0; i < WIDE_TABLE_COLUMN_COUNT; ++i) { setterMethod.invoke(instance, "INTFIELD" + i, random.nextInt()); } } if (null != prevClassLoader) { ClassLoaderStack.setCurrentClassLoader(prevClassLoader); } } private void createWideTable() throws Exception { try (Connection conn = testServer.getConnection(); Statement stmt = conn.createStatement();) { stmt.executeUpdate("DROP TABLE \"" + WIDE_TABLE_NAME + "\" IF EXISTS"); StringBuilder sb = new StringBuilder("CREATE TABLE \"" + WIDE_TABLE_NAME + "\" ("); for (int i = 0; i < WIDE_TABLE_COLUMN_COUNT; ++i) { sb.append("intField" + i + " INT"); if (i < WIDE_TABLE_COLUMN_COUNT - 1) { sb.append(","); } else { sb.append(")"); } } stmt.executeUpdate(sb.toString()); conn.commit(); } } }