sqlite工具类(不完善只有基本功能)

提示:sqlite最好单线程操作(多线程写会冲突),单这个工具我一般用于小的程序用。所以是一个连接在重复使用没有进行保活之类操作。--新增了单例的读写连接持有,通过队列解决多线程sql执行问题。新增了读线程池(保存每个线程)--

maven:

 <properties>
        <itextpdf.version>5.5.6</itextpdf.version>
        <sqlite.jdbc.version>3.27.2.1</sqlite.jdbc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>${sqlite.jdbc.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/org.springframework.jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.jdbc</artifactId>
            <version>3.1.4.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.14</version>
        </dependency>
        <!--pdf操作-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>${itextpdf.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>${itextpdf.version}</version>
        </dependency>
        <!--中文支持-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.16</version>
        </dependency>
<!--druid log支持-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>
        <!-- slf4j核心包,由于上面的桥接包里自动依赖进来了,因此不用加了
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>-->
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.0-alpha5</version>
            <scope>compile</scope>
        </dependency>
        <!-- apache log 4j start -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.17.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.0</version>
        </dependency>
        <!-- end -->
        <!-- durid  -->
        <dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>druid</artifactId>
		    <version> 1.1.23</version>
		</dependency>

代码:

sql替换参数还有一种方法:

                String sqlTemplate = "INSERT INTO traPdfInfo (isComplete,id, oldPath,                 dataType,businessDate,noOrno,suffCode,boxNo,caseNo,pieceNo,fileSize,fileMD5,pagesNum,businessDeptCode,businessDept,fileName) VALUES ('${isComplete}','${id}','${oldPath}','${dataType}','${businessDate}','${noOrno}','${suffCode}','${boxNo}','${caseNo}','${pieceNo}','${fileSize}','${fileMD5}','${pagesNum}','${businessDeptCode}','${businessDept}','${fileName}')";
                HashMap<String,String> hashMapF = new HashMap((int) (14/0.76));
                hashMapF.put("dataType",split[0]);
                hashMapF.put("businessDate",split[1]);
                .......
                StringSubstitutor sub = new StringSubstitutor(hashMapF);
                String resolvedString = sub.replace(sqlTemplate);
package net.kong.yumo.utils;

import cn.hutool.core.text.StrBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteOpenMode;

import java.sql.*;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 不建议多线程修改。
 * sqlite同一时间只能进行一个写操作,当同时有两个写操作的时候,后执行的只能先等待,如果等待时间超过5秒,
 * 就会产生database is locked这种错误.同样一个文件正在写入,重复打开数据库操作更容易导致这种问题的发生。
 * 所以单例持有连接,
 */
public class SqlliteSimConnection   {
    /**
     * 读写连接
     */
    static Connection c=null;
    Statement stmt=null;
    static String url="";
    /**
     *模拟线程池,存放读链接
     */
    static Map<String, Connection> threads=new HashMap<>();
    /**
     * 初始大小 0x7fffffff 2147483647
     */
    static LinkedBlockingDeque<String> sqls = new LinkedBlockingDeque<>();

    public  Connection getThreads() throws SQLException{
        if(threads.get(Thread.currentThread().getName())==null ){
            Connection readCon = getReadCon(this.url);
            setThreads(readCon);
            return readCon;
        } else{
            return threads.get(Thread.currentThread().getName());
        }
    }

    public  void setThreads( Connection thread) {
        SqlliteSimConnection.threads.put(Thread.currentThread().getName(),thread);
    }


    public SqlliteSimConnection(String url) {
        this.url = url;
    }

    /**
     它在对象被垃圾收集器析构(回收)之前调用
      */
    protected void finalize()
    {
        // 在这里终结代码
        close();
    }

    /**
     *
     * @param url java不识别大写的E:盘
     * @return
     */
    private  Connection getReadCon(String url) throws SQLException{
        try {
            Class.forName("org.sqlite.JDBC");
        }catch (Exception e) {
            e.printStackTrace();
        }
        SQLiteConfig config = new SQLiteConfig();
        config.setOpenMode(SQLiteOpenMode.READONLY);//指定数据库连接 读 。

        config.setOpenMode(SQLiteOpenMode.CREATE);//如果数据库不存在,则创建。
        /**
         * 设置数据库连接运行在多线程模式(没有指定单线程模式的情况下)
         * 禁用数据库连接和prepared statement(准备好的语句)上的锁
         * 因此不能在多个线程中并发使用同一个数据库连接
         */
        config.setOpenMode(SQLiteOpenMode.NOMUTEX);//
//        config.setDefaultCacheSize(100000);//页大小默认是 1024Byte,缓存大小默认是 1000 页
        /**
         * 串行模式支持多线程操作,但是必须*统一使用一个全局的数据库连接*.这一点非常重要。串行模式会打开sqlite3所有的锁,在同一时刻保证只有一个线程能访问。这里可以理解为只有一条指向数据库的连接,多个线程的请求将会在该连接上串行传输。
         */
//       config.setOpenMode(SQLiteOpenMode.FULLMUTEX);
//       c = DriverManager.getConnection(url);
        Connection connection = DriverManager.getConnection(url, config.toProperties());

        System.out.println("Opened database successfully");

        return connection;
    }

    public Connection getC() {
        if(c==null) {
            getC1(this.url);
        }
        return c;
    }

    /**
     *
     * @param url java不识别大写的E:盘
     * @return
     */
    private synchronized Connection getC1(String url) {
        if(c==null)   {
            try {
                Class.forName("org.sqlite.JDBC");
                SQLiteConfig config = new SQLiteConfig();
                config.setOpenMode(SQLiteOpenMode.READWRITE);//指定数据库连接可以读写。

                config.setOpenMode(SQLiteOpenMode.CREATE);//如果数据库不存在,则创建。
                /**
                 * 设置数据库连接运行在多线程模式(没有指定单线程模式的情况下)
                 * 禁用数据库连接和prepared statement(准备好的语句)上的锁
                 * 因此不能在多个线程中并发使用同一个数据库连接
                 */
                config.setOpenMode(SQLiteOpenMode.NOMUTEX);
                /**
                 *页大小默认是 1024Byte(1K),缓存大小默认是 1000 页
                 */
                config.setDefaultCacheSize(5000);

                /**
                 * 在max ios支持,
                 * 可以严格保证写入顺序跟提交顺序一致
                 * 设备开发商为了测评数据好看,往往会对提交的数据进行重排,再统一写入,
                 * 亦即写入顺序跟App提交的顺序不一致。在某些情况下,例如断电,就可能导致写入文件不一致的情况,导致文件损坏。
                 */
                config.enableFullSync(true);
                /**
                 * 串行模式支持多线程操作,但是必须*统一使用一个全局的数据库连接*.
                 * 这一点非常重要。串行模式会打开sqlite3所有的锁,在同一时刻保证只有一个线程能访问。
                 * 这里可以理解为只有一条指向数据库的连接,多个线程的请求将会在该连接上串行传输。
                 */
//                config.setOpenMode(SQLiteOpenMode.FULLMUTEX);
                /**
                 * 当synchronous设置为FULL (2), SQLite 数据库引擎在紧急时刻会暂停以确定数据已经写入磁盘。这使 系统崩溃或电源出问题时能确保数据库在重起后不会损坏。FULL synchronous很安全但很慢。
                 *
                 * 当synchronous设置为NORMAL, SQLite数据库引擎在大部分紧急时刻会暂停,但不像FULL模式下那么频繁。 NORMAL模式下有很小的几率(但不是不存在)发生电源故障导致数据库损坏的情况。但实际上,在这种情况 下很可能你的硬盘已经不能使用,或者发生了其他的不可恢复的硬件错误。
                 *
                 * 设置为synchronous OFF (0)时,SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃, 数据不会损伤,但在系统崩溃或写入数据时意外断电的情况下数据库可能会损坏。另一方面,在synchronous OFF时 一些操作可能会快50倍甚至更多。在SQLite 2中,缺省值为NORMAL.而在3中修改为FULL。
                 */
                String property = System.getProperty("db.sqlite.UnSynchronous");
                if ("true".equals(property)) {
                    System.out.println("Synchronous=OFF");
                    System.out.println("SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃, 数据不会损伤,但在系统崩溃或写入数据时意外断电的情况下数据库可能会损坏。另一方面,在synchronous OFF时 一些操作可能会快50倍甚至更多。");
                    config.setSynchronous(SQLiteConfig.SynchronousMode.OFF);
                } else {
                    config.setSynchronous(SQLiteConfig.SynchronousMode.FULL);
                }
                DriverManager.getConnection(url,config.toProperties());
                stmt = c.createStatement();

                System.out.println("Opened database successfully");
            } catch (Exception e) {
                System.out.println("Opened database faile");
                e.printStackTrace();
            }
        }

        return c;
    }
    /**
     * 连接到一个现有的数据库。如果数据库不存在, 那么它就会被创建,最后将返回一个数据库对象。
     */
    public void Connection() {
        c =  getC();
    }

    public void close() {
        try {
            if (!sqls.isEmpty()) {
                exe();
            }
            stmt.close();
            c.close();
            threads.clear();
            sqls.clear();
//            System.out.println("close database successfully");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void addSql(String sql) throws SQLException {
        if (sql.endsWith(";")) {
            sqls.addLast(sql);
        }else {
            sqls.addLast(sql+";");
        }
    }
    public void addSqls(List<String> _sqls) throws SQLException {
        for (String sql : _sqls) {
            if (sql.endsWith(";")) {
                sqls.addLast(sql);
            }else {
                sqls.addLast(sql+";");
            }
        }
    }
    /**
     * 使用时注意 多线程写
     * @return
     * @throws SQLException
     */
    public void insert(String sql) throws SQLException {
            stmt.executeUpdate(sql);
    }

    /**
     * sql替换参数用 :: 包裹
     * 字符串类型自动加单引号  使用时注意 多线程写
     * @param sql selcet id from t1 where id=:id: and cd=:cd:
     * @param par {"id": "dsgds" ,"cd": 1567454}
     * @throws SQLException
     */
    public void insert(String sql,Map<String,Object> par) throws SQLException {
        String[] split = sql.split(":");
        StringBuilder newString= new StringBuilder();
        for (int i = 0; i < split.length; i++) {
            if (i%2==0) {
                newString.append(split[i]);
            }else {
                if (par.get(split[i]) instanceof String) {
                    newString.append("'");
                    newString.append(String.valueOf(par.get(split[i])));
                    newString.append("'");
                }else {
                    if (par.get(split[i]) != null) {
                        newString.append(String.valueOf(par.get(split[i])));
                    }else {
                        throw new IllegalArgumentException("参数缺失:"+split[i]);
                    }
                }
            }
        }
        c.setAutoCommit(false);
        stmt.executeUpdate(newString.toString());
        c.commit();
    }
    /**
     * 使用时注意 多线程写
     * @return
     * @throws SQLException
     */
    public void insertBatch(String sql,List<Map<String,Object>> par) throws SQLException {
        String[] split = sql.split(":");
        PreparedStatement preparedStatement = c.prepareStatement(sql);
        StrBuilder builder = StrBuilder.create();
        for (Map<String, Object> stringObjectMap : par) {

            for (int i = 0; i < split.length; i++) {
                if (i%2==0) {
                    builder.append(split[i]);
                }else {
                    if (stringObjectMap.get(split[i]) instanceof String) {
                        builder.append("'");
                        builder.append(String.valueOf(stringObjectMap.get(split[i])));
                        builder.append("'");
                    }else {
                        if (stringObjectMap.get(split[i]) != null) {
                            builder.append(String.valueOf(stringObjectMap.get(split[i])));
                        }else {
                            throw new IllegalArgumentException("参数缺失:"+split[i]);
                        }
                    }
                }
            }
            preparedStatement.addBatch(builder.toString());
            builder.reset();
        }

        c.setAutoCommit(false);

        int [] counts = preparedStatement.executeBatch();

        c.commit();
    }


    public int[] exe() throws SQLException {
        int[] ints=null;
        try {
            Statement statement = c.createStatement();
            c.setAutoCommit(false);
            int size = this.sqls.size();
            for (int i = size; i > 0; i--) {
                statement.addBatch(this.sqls.poll());
            }
            ints = statement.executeBatch();
            c.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            c.rollback();
        }
        return ints;
    }
    /**
     * 使用时注意 多线程写
     * @return
     * @throws SQLException
     */
    public int[] insert(List<String> sqls) throws SQLException {
        int[] ints=null;
        try {
                c.setAutoCommit(false);
                for (String sql:sqls) {
                    stmt.addBatch(sql);
                }
               ints = stmt.executeBatch();
                c.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            c.rollback();
        }
        return ints;
    }

    /**
     * 使用时注意 多线程写
     * @return
     * @throws SQLException
     *
     * @param sqls sql数组
     * @param batchNum 每多少条提交一次事务 建议大于1万
     * @throws SQLException
     */
    public void batchInsert(List<String> sqls,int batchNum) throws SQLException {
        for (int i = 0; i < sqls.size();) {
            c.setAutoCommit(false);
            for (; (i % batchNum) == 0 &&  i < sqls.size() ; i++) {
                stmt.addBatch(sqls.get(i));
            }
            stmt.executeBatch();
            c.commit();
        }
    }


    public ResultSet select(String sql) throws SQLException {
        return stmt.executeQuery(sql);
    }
    public List<Map<String, Object>> selectList(String sql) throws SQLException {
        return convertList(stmt.executeQuery(sql));
    }
    public Map<String, Object> selectMap(String sql) throws SQLException {
        return convertMap(stmt.executeQuery(sql));
    }
    public int update( String sql) throws SQLException {
        return stmt.executeUpdate(sql);
    }
    /**
     * 执行sql脚本文件 使用Spring工具类
     */
    public void runSqlBySpringUtils(String sqlPath) throws Exception {
        try {
//            FileSystemResource rc = new FileSystemResource(sqlPath);
            ClassPathResource rc = new ClassPathResource(sqlPath );

             EncodedResource er = new EncodedResource(  rc, "utf-8");
             ScriptUtils.executeSqlScript(c,  rc);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }
    public static List<Map<String, Object>> convertList(ResultSet rs) {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        try {
            ResultSetMetaData md = rs.getMetaData();
            int columnCount = md.getColumnCount();
            while (rs.next()) {
                Map<String, Object> rowData = new HashMap<String, Object>();
                for (int i = 1; i <= columnCount; i++) {
                    rowData.put(md.getColumnName(i), rs.getObject(i));
                }
                list.add(rowData);
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
                rs = null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return list;
    }


    public static Map<String, Object> convertMap(ResultSet rs){
        Map<String, Object> map = new TreeMap<String, Object>();
        try{
            ResultSetMetaData md = rs.getMetaData();
            int columnCount = md.getColumnCount();
            while (rs.next()) {
                for (int i = 1; i <= columnCount; i++) {
                    map.put(md.getColumnName(i), rs.getObject(i));
                }
            }
        } catch (SQLException e){
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
                rs = null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return map;
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值