工作中遇到一個需求,就是需要把mysql里的一些表數據生成db文件,給客戶端使用,客戶端使用sqlite數據庫;
首先我們需要在項目中添加Sqlite JDBC 依賴
<dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.23.1</version> </dependency>
生成DB文件工具類
package cn.kayun.util; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * Created by root on 2018/7/2 0002. */ public class DBUtil { private static final Logger logger = LoggerFactory.getLogger(DBUtil.class); private static Connection connection = null; private static Statement statement = null; private static String username = "kanyun"; private static String password = "kanyun"; /** * @describe: 設置連接 * @params: * @Author: Kanyun * @Date: 2018/7/12 9:54 */ public static void setConnection() { try { // 聲明驅動類型 Class.forName("org.sqlite.JDBC"); // 設置sqlite db文件存放基本目錄 String path = DBUtil.class.getClassLoader().getResource("").getPath(); // 設置 sqlite文件路徑,等同於mysql連接地址(jdbc:mysql://127.0.0.1:3306/test) String url = "jdbc:sqlite:" + path + "data.db"; // 獲取連接 connection = DriverManager.getConnection(url, username, password); // 聲明 statement = connection.createStatement(); } catch (Exception e) { throw new RuntimeException("建立Sqlite連接失敗"); } } /** * @describe: 創建表 * @params: tableName: 要創建的表的名稱 className:項目中Pojo的類名(需要注意的是該類名需要加上包名 如 com.xxx.xxx.pojo.xxx) * @Author: Kanyun * @Date: 2018/7/12 9:56 */ public synchronized static void create(String tableName, String className) { try { statement.executeUpdate("DROP TABLE IF EXISTS " + tableName + ";"); // 通過反射,獲取傳入類名的字段信息 Field[] fields = Class.forName(className).getDeclaredFields(); StringBuffer sb = new StringBuffer(); String reg = " "; sb.append("create table " + tableName + " ("); for (Field field : fields) { // 設置使用反射時,可以訪問私有變量,當pojo的變量設置為private修飾時,isAccessible()得到的值是false,必須要改成true才可以訪問 field.setAccessible(true); // 通過獲得的字段,獲取字段類型,和字段名,來定義創建表的字段名和字段類型 if (field.getType().getName().equals(java.lang.Long.class.getName())) { sb.append(reg + field.getName() + " bigint(20) "); } else if (field.getType().getName().equals(java.lang.String.class.getName())) { sb.append(reg + field.getName() + " varchar(255) "); } else if (field.getType().getName().equals(java.util.Date.class.getName())) { sb.append(reg + field.getName() + " datetime "); } else { sb.append(reg + field.getName() + " int(11) "); } reg = ","; } sb.append(") ;"); statement.executeUpdate(sb.toString()); } catch (Exception e) { logger.error("建表失敗:" + e); throw new RuntimeException("建表失敗,表名稱:" + tableName); } } /** * @describe: 表中插入數據 * @params: tableName:表名 list:待插入的對象集合 需要注意的是插入的對象要跟表名對應 * @Author: Kanyun * @Date: 2018/7/12 10:03 */ public synchronized static <T> int insert(String tableName, List<T> list) { StringBuffer declaration = new StringBuffer(); List<String> dataSqlArray = new ArrayList<>(); int count = 0; try { list.stream().map(t -> { if (dataSqlArray.size() < 1) { logger.info("定義語句"); Field[] fields = t.getClass().getDeclaredFields(); declaration.append(" ("); String reg = ""; for (Field field : fields) { field.setAccessible(true); declaration.append(reg + field.getName()); reg = ","; } declaration.append(")"); } return t; }).forEach((t) -> { Field[] fields = t.getClass().getDeclaredFields(); StringBuffer oneData = new StringBuffer(); String reg = ""; oneData.append(" ("); for (Field field : fields) { try { field.setAccessible(true); if (null == field.get(t) || "".equals(field.get(t))) { oneData.append(reg + null); } else if (field.getType().getName().equals(java.lang.String.class.getName())) { // 傳入的對象中字段是字符串時,插入數據庫需要轉義單引號 oneData.append(reg + "\'" + field.get(t) + "\'"); // 傳入的對象中字段是日期類型時,先將日期轉換為 yyyy-MM-dd 的形式(我這里,傳入對象的日期對象格式為:Wed Jul 11 14:34:07 CST 2018),在將其轉換成字符串,注意轉義單引號 } else if (field.getType().getName().equals(java.util.Date.class.getName())) { String dd = field.get(t).toString(); SimpleDateFormat sdf1 = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = sdf2.format(sdf1.parse(dd)); oneData.append(reg + "\'" + date + "\'"); } else { oneData.append(reg + field.get(t)); } reg = ","; } catch (Exception e) { e.printStackTrace(); } } oneData.append(")"); dataSqlArray.add(oneData.toString()); }); String dataSql = StringUtils.join(dataSqlArray, ","); String retSQL = "INSERT INTO " + tableName + declaration.toString() + " VALUES " + dataSql + ";"; System.out.println(retSQL); PreparedStatement prep = connection.prepareStatement(retSQL); // 設置自動提交 connection.setAutoCommit(true); count = prep.executeUpdate(); } catch (Exception e) { logger.error("插入失敗:" + e); e.printStackTrace(); } return count; } /** * @describe: 關閉鏈接 * @params: * @Author: Kanyun * @Date: 2018/7/12 10:11 */ public static void endConnection() { try { connection.close(); } catch (Exception e) { e.printStackTrace(); } } }
生成的DB文件在classess目錄下.
需要注意的是,在創建鏈接的過程中,雖然可以傳入用戶名,密碼,但是使用一些工具(比如Navicat)還是可以查看db文件的內容的,因為免費版的SQLite有一個致命缺點:不支持加密
如果需要使用sqlite加密,一是數據本身進行加密,也就是往表中插入的數據進行加密,二是可以加密db文件本身
更多加密方式,參考:https://www.cnblogs.com/ligun123/p/5206942.html
