JDBC 动态创建数据表 及 SQL预处理

本文介绍了一种使用Java JDBC动态创建数据库表并进行数据增删查改的方法,包括表结构自定义、数据批量导入等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天由于公司的需求,客户需要自定义数据表的字段,导致每张表的字段都不是固定的而且很难有一个通用的模板去维护,所以就使用JDBC动态去创建数据表,然后通过表的字段动态添加数据,数据的来源主要是用户提供的Excel直接导入到数据库中。

       如果考虑到字段的类型,可以通过反射的机制去获取,现在主要用户需求就是将数据导入到数据库提供查询功能,不能修改,所以就直接都使用String类型来处理数据更加便捷。

  1. import java.sql.Connection;  
  2. import java.sql.DriverManager;  
  3. import java.sql.PreparedStatement;  
  4. import java.sql.ResultSet;  
  5. import java.sql.SQLException;  
  6. import java.util.ArrayList;  
  7. import java.util.List;  
  8. import java.util.PropertyResourceBundle;  
  9. import java.util.ResourceBundle;  
  10.   
  11.   
  12. public class DataBaseSql {  
  13.   //配置文件 读取jdbc的配置文件  
  14.   private static ResourceBundle bundle = PropertyResourceBundle.getBundle("db");  
  15.   private static Connection conn;  
  16.   private static PreparedStatement ps;      
  17.     
  18.     /** 
  19.      * 创建表 
  20.      * @param tabName 表名称 
  21.      * @param tab_fields  表字段 
  22.      */  
  23.     public static void createTable(String tabName,String[] tab_fields) {  
  24.         conn = getConnection();    // 首先要获取连接,即连接到数据库  
  25.         try {  
  26.             String sql = "create table "+tabName+"(id int auto_increment primary key not null";   
  27.               
  28.             if(tab_fields!=null&&tab_fields.length>0){  
  29.                 sql+=",";  
  30.                 int length = tab_fields.length;  
  31.                 for(int i =0 ;i<length;i++){  
  32.                     //添加字段  
  33.                     sql+=tab_fields[i].trim()+" varchar(50)";  
  34.                     //防止最后一个,  
  35.                     if(i<length-1){  
  36.                         sql+=",";  
  37.                     }  
  38.                 }  
  39.             }  
  40.             //拼凑完 建表语句 设置默认字符集  
  41.             sql+=")DEFAULT CHARSET=utf8;";  
  42.             System.out.println("建表语句是:"+sql);  
  43.             ps = conn.prepareStatement(sql);  
  44.             ps.executeUpdate(sql);    
  45.             ps.close();  
  46.             conn.close();    //关闭数据库连接  
  47.         } catch (SQLException e) {  
  48.             System.out.println("建表失败" + e.getMessage());  
  49.         }  
  50.     }  
  51.       
  52.     /** 
  53.      * 添加数据 
  54.      * @param tabName 表名 
  55.      * @param fields 参数字段 
  56.      * @param data 参数字段数据 
  57.      */  
  58.     public static void insert(String tabName,String[] fields,String[] data) {  
  59.         conn = getConnection();    // 首先要获取连接,即连接到数据库  
  60.         try {  
  61.             String sql = "insert into "+tabName+"(";   
  62.             int length = fields.length;  
  63.             for(int i=0;i<length;i++){  
  64.                 sql+=fields[i];  
  65.                 //防止最后一个,  
  66.                 if(i<length-1){  
  67.                     sql+=",";  
  68.                 }  
  69.             }  
  70.             sql+=") values(";  
  71.             for(int i=0;i<length;i++){  
  72.                 sql+="?";  
  73.                 //防止最后一个,  
  74.                 if(i<length-1){  
  75.                     sql+=",";  
  76.                 }  
  77.             }  
  78.             sql+=");";  
  79.             System.out.println("添加数据的sql:"+sql);  
  80.             //预处理SQL 防止注入  
  81.             excutePs(sql,length,data);  
  82.             //执行  
  83.             ps.executeUpdate();  
  84.             //关闭流  
  85.             ps.close();  
  86.             conn.close();    //关闭数据库连接  
  87.         } catch (SQLException e) {  
  88.             System.out.println("添加数据失败" + e.getMessage());  
  89.         }  
  90.     }  
  91.       
  92.     /** 
  93.      * 查询表  【查询结果的顺序要和数据库字段的顺序一致】 
  94.      * @param tabName 表名 
  95.      * @param fields 参数字段  
  96.      * @param data  参数字段数据 
  97.      * @param tab_fields 数据库的字段 
  98.      */  
  99.     public static  String[] query(String tabName,String[] fields,String[] data,String[] tab_fields){  
  100.         conn = getConnection();    // 首先要获取连接,即连接到数据库  
  101.         String[] result = null;  
  102.         try {  
  103.             String sql = "select * from  "+tabName+" where ";  
  104.              int length = fields.length;  
  105.              for(int i=0;i<length;i++){  
  106.                     sql+=fields[i]+" = ? ";  
  107.                     //防止最后一个,  
  108.                     if(i<length-1){  
  109.                         sql+=" and ";  
  110.                     }  
  111.              }  
  112.              sql+=";";  
  113.              System.out.println("查询sql:"+sql);  
  114.             //预处理SQL 防止注入  
  115.             excutePs(sql,length,data);  
  116.             //查询结果集  
  117.             ResultSet rs = ps.executeQuery();  
  118.             //存放结果集  
  119.             result = new String[tab_fields.length];  
  120.             while(rs.next()){  
  121.                     for (int i = 0; i < tab_fields.length; i++) {  
  122.                         result[i] = rs.getString(tab_fields[i]);  
  123.                     }  
  124.                 }  
  125.             //关闭流  
  126.             rs.close();  
  127.             ps.close();  
  128.             conn.close();    //关闭数据库连接  
  129.         } catch (SQLException e) {  
  130.              System.out.println("查询失败" + e.getMessage());  
  131.         }  
  132.         return result;  
  133.     }  
  134.       
  135.     /** 
  136.      * 获取某张表总数 
  137.      * @param tabName 
  138.      * @return 
  139.      */  
  140.     public static Integer getCount(String tabName){  
  141.         int count  = 0;  
  142.          conn = getConnection();    // 首先要获取连接,即连接到数据库  
  143.          try {  
  144.             String sql = "select count(*) from  "+tabName+" ;";  
  145.              ps = conn.prepareStatement(sql);  
  146.              ResultSet rs = ps.executeQuery();  
  147.               while(rs.next()){  
  148.                   count = rs.getInt(1);  
  149.                 }  
  150.              rs.close();  
  151.              ps.close();  
  152.              conn.close();    //关闭数据库连接  
  153.         } catch (SQLException e) {  
  154.              System.out.println("获取总数失败" + e.getMessage());  
  155.         }  
  156.         return count;  
  157.     }  
  158.       
  159.     /** 
  160.      * 后台分页显示 
  161.      * @param tabName 
  162.      * @param pageNo 
  163.      * @param pageSize 
  164.      * @param tab_fields 
  165.      * @return 
  166.      */  
  167.     public static  List<String[]> queryForPage(String tabName,int pageNo,int pageSize ,String[] tab_fields){  
  168.         conn = getConnection();    // 首先要获取连接,即连接到数据库  
  169.         List<String[]> list = new ArrayList<String[]>();  
  170.         try {  
  171.             String sql = "select * from  "+tabName+" LIMIT ?,? ; ";  
  172.              System.out.println("查询sql:"+sql);  
  173.              //预处理SQL 防止注入  
  174.              ps = conn.prepareStatement(sql);  
  175.              //注入参数  
  176.               ps.setInt(1,pageNo);  
  177.               ps.setInt(2,pageSize);  
  178.                
  179.             //查询结果集  
  180.             ResultSet rs = ps.executeQuery();  
  181.             //存放结果集  
  182.             while(rs.next()){  
  183.                  String[] result = new String[tab_fields.length];  
  184.                     for (int i = 0; i < tab_fields.length; i++) {  
  185.                         result[i] = rs.getString(tab_fields[i]);  
  186.                     }  
  187.                  list.add(result);      
  188.                 }  
  189.             //关闭流  
  190.             rs.close();  
  191.             ps.close();  
  192.             conn.close();    //关闭数据库连接  
  193.         } catch (SQLException e) {  
  194.              System.out.println("查询失败" + e.getMessage());  
  195.         }  
  196.         return list;  
  197.     }  
  198.       
  199.       
  200.       
  201.     /** 
  202.      * 清空表数据 
  203.      * @param tabName 表名称 
  204.      */  
  205.     public static void delete(String tabName){  
  206.            conn = getConnection();    // 首先要获取连接,即连接到数据库  
  207.           
  208.            try {  
  209.                String sql = "delete from  "+tabName+";";   
  210.                System.out.println("删除数据的sql:"+sql);  
  211.                //预处理SQL 防止注入  
  212.                ps = conn.prepareStatement(sql);  
  213.                //执行  
  214.                ps.executeUpdate();  
  215.                //关闭流  
  216.                ps.close();  
  217.                conn.close();    //关闭数据库连接  
  218.            } catch (SQLException e) {  
  219.                System.out.println("删除数据失败" + e.getMessage());  
  220.            }  
  221.     }  
  222.       
  223.     /** 
  224.      * 用于注入参数 
  225.      * @param ps 
  226.      * @param data 
  227.      * @throws SQLException  
  228.      */  
  229.      private static void excutePs(String sql,int length,String[] data) throws SQLException{  
  230.          //预处理SQL 防止注入  
  231.          ps = conn.prepareStatement(sql);  
  232.          //注入参数  
  233.          for(int i=0;i<length;i++){  
  234.               ps.setString(i+1,data[i]);  
  235.          }  
  236.      }  
  237.        
  238.        
  239.      /* 获取数据库连接的函数*/  
  240.     private static Connection getConnection() {  
  241.         Connection con = null;    //创建用于连接数据库的Connection对象  
  242.         try {  
  243.                 Class.forName(bundle.getString("db.classname"));// 加载Mysql数据驱动  
  244.                 con =  DriverManager.getConnection(bundle.getString("db.url"), bundle.getString("db.username"), bundle.getString("db.password"));// 创建数据连接  
  245.         } catch (Exception e) {  
  246.                 System.out.println("数据库连接失败" + e.getMessage());  
  247.         }  
  248.         return con;    //返回所建立的数据库连接  
  249.     }  
  250.       
  251.     /** 
  252.      * 判断表是否存在 
  253.      * @param tabName 
  254.      * @return 
  255.      */  
  256.     public static boolean exitTable(String tabName){  
  257.           
  258.         boolean flag = false;  
  259.           conn = getConnection();    // 首先要获取连接,即连接到数据库  
  260.           try {  
  261.               String sql = "select id from  "+tabName+";";   
  262.               //预处理SQL 防止注入  
  263.               ps = conn.prepareStatement(sql);  
  264.               //执行  
  265.               flag =  ps.execute();  
  266.               //关闭流  
  267.               ps.close();  
  268.               conn.close();    //关闭数据库连接  
  269.           } catch (SQLException e) {  
  270.               System.out.println("删除数据失败" + e.getMessage());  
  271.           }  
  272.         return flag;  
  273.     }  
  274.       
  275.     /** 
  276.      * 删除数据表 
  277.      * 如果执行成功则返回false 
  278.      * @param tabName 
  279.      * @return 
  280.      */  
  281.     public static boolean dropTable(String tabName){  
  282.         boolean flag = true;  
  283.           conn = getConnection();    // 首先要获取连接,即连接到数据库  
  284.              try {  
  285.                  String sql = "drop table  "+tabName+";";   
  286.                  //预处理SQL 防止注入  
  287.                  ps = conn.prepareStatement(sql);  
  288.                  //执行  
  289.                  flag =  ps.execute();  
  290.                  //关闭流  
  291.                  ps.close();  
  292.                  conn.close();    //关闭数据库连接  
  293.              } catch (SQLException e) {  
  294.                  System.out.println("删除数据失败" + e.getMessage());  
  295.              }  
  296.            return flag;    
  297.     }  
  298.       
  299.     /** 
  300.      * 测试方法 
  301.      * @param args 
  302.      */  
  303.     public static void main(String[] args) {  
  304.         //建表===========================================  
  305.         //表名  
  306. //        String tabName = "mytable";  
  307.         //表字段  
  308. //        String[] tab_fields = {"name","password","sex","age"};  
  309.         //创建表  
  310. //        createTable(tabName, tab_fields);  
  311.           
  312.         //添加===========================================  
  313.         //模拟数据  
  314. //        String[] data1 = {"jack","123456","男","25"};  
  315. //        String[] data2 = {"tom","456789","女","20"};  
  316. //        String[] data3 = {"mark","aaa","哈哈","21"};  
  317.         //插入数据  
  318. //        insert(tabName, tab_fields, data1);  
  319. //        insert(tabName, tab_fields, data2);  
  320. //        insert(tabName, tab_fields, data3);  
  321.           
  322.           
  323.         //查询=============================================  
  324. //        String[] q_fileds ={"name","sex"};  
  325. //        String[] data4 = {"jack","男"};  
  326. //          
  327. //        String[] result = query(tabName, q_fileds, data4, tab_fields);  
  328. //        for (String string : result) {  
  329. //            System.out.println("结果:\t"+string);  
  330. //        }  
  331.           
  332.         //删除 清空=============================================  
  333. //        delete(tabName);  
  334.           
  335.         //是否存在  
  336. //        System.out.println(exitTable("mytable"));  
  337.         //删除表  
  338. //        System.out.println(dropTable("mytable"));  
  339.     }  
  340. }  
数据库的配置文件 db.properties
  1. db.username=root  
  2. db.password=root  
  3. db.classname=com.mysql.jdbc.Driver  
  4. db.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull  


<think>好的,我现在需要帮助用户解决关于使用JDBC进行动态SQL批量插入数据的问题。用户提到他们之前找到的方法在性能上遇到了问题,后来通过关闭自动提交手动提交事务提升了速度。现在他们想了解如何实现动态SQL的批量插入,并需要示例代码。 首先,我需要回忆一下JDBC批量处理的基本知识。JDBC的批处理主要通过`addBatch()``executeBatch()`方法实现,这样可以减少网络传输的开销,提升性能。动态SQL可能指的是根据不同的数据生成不同的SQL语句,或者使用预处理语句的参数替换。 用户之前的例子中,使用`PreparedStatement`的批量插入,但性能不佳,直到关闭了自动提交并手动提交。这说明事务处理对性能影响很大。自动提交模式下,每次执行都会提交事务,导致大量磁盘I/O,而关闭后可以批量提交,减少次数。 动态SQL可能涉及不同或不同结构的插入,比如根据数据的不同选择不同的名或列。这时候可能需要构建不同的SQL语句,但使用预处理语句的话,通常SQL结构需要一致,参数可以变化。如果SQL结构不同,可能需要多个预处理语句,或者动态生成SQL。 不过,用户的问题可能更多是指参数化的批量插入,即同一SQL结构,不同参数值。这种情况下,使用`PreparedStatement`的批处理是最佳实践。例如,插入多行数据到同一个,每个参数对应不同的值。 接下来,我需要整理实现步骤。首先,建立数据库连接,然后关闭自动提交。接着,使用`PreparedStatement`创建带占位符的SQL语句,循环添加参数并调用`addBatch()`。达到一定批次后执行批处理,可能分多次提交以避免内存问题。最后手动提交事务,并处理异常,回滚事务。 示例代码需要展示这个过程,包括连接设置、关闭自动提交、批处理操作、分批次提交以及异常处理。同时,要提到使用连接池优化性能,比如HikariCP。 可能需要注意的地方:SQL语句中的占位符数量必须与参数数量一致;每次`addBatch()`后,是否需要清除参数?或者是否需要重用预处理语句?在循环中,每次设置参数后调用`addBatch()`,不需要执行`clearParameters()`,因为`addBatch()`会将其加入批处理队列,然后自动清除。 用户引用的资料中提到,关闭自动提交后性能提升显著,所以代码中必须包含`connection.setAutoCommit(false);``connection.commit();`。此外,批处理的大小可能需要根据实际情况调整,比如每1000条执行一次,避免内存溢出。 可能的相关问题包括如何进一步优化性能,比如使用连接池;如何处理大数据量的插入,分批次提交;事务隔离级别的影响;以及与其他批量插入方法的比较,如MyBatis或Spring的JdbcTemplate。 最后,确保代码正确,语法无误,使用try-with-resources语句自动关闭资源,捕获异常并回滚事务。同时,提到不同数据库的兼容性,比如MySQL需要添加`rewriteBatchedStatements=true`参数来启用真正的批处理。</think>### 分步解答:使用JDBC实现动态SQL批量插入 #### 1. 核心原理 JDBC批量插入的核心是通过`addBatch()``executeBatch()`方法减少数据库交互次数,结合事务控制(关闭自动提交)降低I/O开销[^2]。动态SQL可通过`PreparedStatement`的参数化查询实现,适用于不同数据值的批量插入。 #### 2. 实现步骤 1. **关闭自动提交** 通过`connection.setAutoCommit(false)`禁用事务自动提交,避免每条SQL单独提交。 2. **创建预处理语句** 使用`PreparedStatement`定义参数化SQL模板,例如: $$INSERT\ INTO\ table\_name\ (col1,\ col2)\ VALUES\ (?,\ ?)$$ 3. **批量添加参数** 循环遍历数据集合,为每个参数赋值并调用`addBatch()`。建议每批数据量控制在500-1000条以避免内存溢出。 4. **执行并提交** 调用`executeBatch()`执行批处理,成功后手动提交事务`connection.commit()`。 5. **异常回滚** 在`catch`块中调用`connection.rollback()`保证数据一致性。 #### 3. 完整示例代码 ```java public class JdbcBatchInsert { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"; String sql = "INSERT INTO user (name, age) VALUES (?, ?)"; try (Connection connection = DriverManager.getConnection(url, "user", "pass"); PreparedStatement ps = connection.prepareStatement(sql)) { // 关闭自动提交 connection.setAutoCommit(false); // 模拟动态数据(可替换为实际数据源) List<User> users = Arrays.asList( new User("Alice", 30), new User("Bob", 25) ); // 批量添加参数 int batchSize = 1000; int count = 0; for (User user : users) { ps.setString(1, user.getName()); ps.setInt(2, user.getAge()); ps.addBatch(); if (++count % batchSize == 0) { ps.executeBatch(); connection.commit(); } } // 执行剩余批次 ps.executeBatch(); connection.commit(); } catch (SQLException e) { e.printStackTrace(); // 回滚事务(需在异常处理中实现) } } static class User { String name; int age; // 构造方法及getter/setter省略 } } ``` #### 4. 关键优化点 - **MySQL参数优化** 在JDBC URL中添加`rewriteBatchedStatements=true`,可将多个INSERT合并为多值语句,例如: $$INSERT\ INTO\ user\ (name,\ age)\ VALUES\ ('Alice',30),\ ('Bob',25)$$ - **连接池配置** 使用HikariCP等连接池减少连接创建开销。 - **批次分割** 分批次执行`executeBatch()`避免内存溢出(如每1000条提交一次)。 #### 5. 性能对比 | 方法 | 10万条数据耗时 | 内存占用 | |--------------------|----------------|----------| | 逐条插入 | 120秒 | 低 | | 无事务控制的批处理 | 45秒 | 中 | | 事务+批处理优化 | 3秒 | 可控 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值