SQLite – Java
                           
天天向上
发布: 2025-03-05 23:06:02

原创
186 人浏览过

在 Java 中,SQLite 通常通过 JDBC(Java Database Connectivity) 进行操作,使用 sqlite-jdbc 驱动 来连接和操作 SQLite 数据库。以下是详细的讲解,包括 数据库连接、执行 SQL 语句、预编译查询、事务管理、BLOB 处理、多线程支持等


1. 添加 SQLite 依赖

Maven 依赖

如果你使用 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.42.0.0</version>  <!-- 最新版本请查看官方 -->
</dependency>

如果不使用 Maven,可以直接下载 sqlite-jdbc.jar,并手动添加到 classpath


2. 连接 SQLite 数据库

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SQLiteConnection {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";  // 数据库文件

        try (Connection conn = DriverManager.getConnection(url)) {
            if (conn != null) {
                System.out.println("成功连接到 SQLite 数据库!");
            }
        } catch (SQLException e) {
            System.out.println("连接失败: " + e.getMessage());
        }
    }
}

关键点

  • jdbc:sqlite:test.db:表示连接 test.db 文件(如果文件不存在,会自动创建)。
  • DriverManager.getConnection(url):获取数据库连接。
  • try-with-resources 结构:自动关闭 Connection 资源,避免泄漏。

3. 创建表

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class CreateTable {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        String sql = "CREATE TABLE IF NOT EXISTS users ("
                   + "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   + "name TEXT NOT NULL, "
                   + "age INTEGER);";

        try (Connection conn = DriverManager.getConnection(url);
             Statement stmt = conn.createStatement()) {
            stmt.execute(sql);
            System.out.println("数据表创建成功!");
        } catch (SQLException e) {
            System.out.println("创建表失败: " + e.getMessage());
        }
    }
}

CREATE TABLE IF NOT EXISTS

  • 防止重复创建表
  • id自动递增主键
  • name必填字段
  • age整数字段

4. 插入数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class InsertData {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        String sql = "INSERT INTO users (name, age) VALUES ('Alice', 25);";

        try (Connection conn = DriverManager.getConnection(url);
             Statement stmt = conn.createStatement()) {
            stmt.executeUpdate(sql);
            System.out.println("数据插入成功!");
        } catch (SQLException e) {
            System.out.println("插入失败: " + e.getMessage());
        }
    }
}

5. 查询数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SelectData {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        String sql = "SELECT id, name, age FROM users";

        try (Connection conn = DriverManager.getConnection(url);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
            }
        } catch (SQLException e) {
            System.out.println("查询失败: " + e.getMessage());
        }
    }
}

6. 使用预编译 SQL(防止 SQL 注入)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PreparedInsert {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        String sql = "INSERT INTO users (name, age) VALUES (?, ?)";

        try (Connection conn = DriverManager.getConnection(url);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setString(1, "Bob");
            pstmt.setInt(2, 30);
            pstmt.executeUpdate();

            System.out.println("数据插入成功!");
        } catch (SQLException e) {
            System.out.println("插入失败: " + e.getMessage());
        }
    }
}

? 占位符:可以防止 SQL 注入攻击。


7. 事务管理

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class TransactionExample {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        try (Connection conn = DriverManager.getConnection(url)) {
            conn.setAutoCommit(false);  // 关闭自动提交,启用事务

            try (Statement stmt = conn.createStatement()) {
                stmt.executeUpdate("INSERT INTO users (name, age) VALUES ('Tom', 35);");
                stmt.executeUpdate("INSERT INTO users (name, age) VALUES ('Jerry', 28);");

                conn.commit();  // 提交事务
                System.out.println("事务提交成功!");
            } catch (SQLException e) {
                conn.rollback();  // 发生错误时回滚
                System.out.println("事务回滚: " + e.getMessage());
            }
        } catch (SQLException e) {
            System.out.println("数据库连接失败: " + e.getMessage());
        }
    }
}

8. 处理 BLOB(存储图片、文件等)

import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class InsertBlob {
    public static void main(String[] args) {
        String url = "jdbc:sqlite:test.db";

        String sql = "INSERT INTO files (name, data) VALUES (?, ?)";

        try (Connection conn = DriverManager.getConnection(url);
             PreparedStatement pstmt = conn.prepareStatement(sql);
             FileInputStream fis = new FileInputStream(new File("image.jpg"))) {

            pstmt.setString(1, "image.jpg");
            pstmt.setBinaryStream(2, fis, (int) new File("image.jpg").length());
            pstmt.executeUpdate();

            System.out.println("BLOB 数据插入成功!");
        } catch (SQLException | java.io.IOException e) {
            System.out.println("插入失败: " + e.getMessage());
        }
    }
}

9. 多线程支持

默认情况下,SQLite 不支持多个线程同时写入,如果需要支持并发写入:

Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db?journal_mode=WAL");
  • WAL(Write-Ahead Logging) 模式可以让多个线程读写操作更加稳定。

10. SQLite 在 Java 中的高级优化

在 Java 中使用 SQLite 时,性能优化是一个关键问题,特别是当数据量增大时。下面介绍一些优化技巧,包括 索引优化、批量插入、WAL 模式、缓存优化、pragma 指令等


10.1. 使用索引提高查询性能

当数据表中的数据量较大时,合理创建索引可以 大幅提高查询速度

String sql = "CREATE INDEX IF NOT EXISTS idx_users_name ON users(name);";

示例:查询使用索引

String sql = "SELECT * FROM users WHERE name = 'Alice';";

name 列上创建索引后,查询速度会显著提高。

索引优化建议

  • 频繁查询的列 应该建立索引,例如 WHERE 条件中的列。
  • 避免过多索引,索引会影响 INSERTUPDATE 的性能。
  • 使用 EXPLAIN QUERY PLAN 分析查询计划:
  String sql = "EXPLAIN QUERY PLAN SELECT * FROM users WHERE name = 'Alice';";

10.2. 启用 WAL(Write-Ahead Logging)模式

默认情况下,SQLite 使用 “DELETE” 日志模式,在并发写入时可能导致锁冲突。启用 WAL 模式 可以提高 并发读写性能

try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db")) {
    try (Statement stmt = conn.createStatement()) {
        stmt.execute("PRAGMA journal_mode=WAL;");
        System.out.println("WAL 模式已启用");
    }
} catch (SQLException e) {
    System.out.println("启用 WAL 失败: " + e.getMessage());
}

WAL 优势

  • 读写操作可同时进行(提高并发)。
  • 适用于多线程 Java 程序。

10.3. 批量插入数据

批量插入比单条插入快 10~100 倍,因为 SQLite 默认每次 INSERT 操作都会进行一次事务提交

错误的方式(慢)

for (int i = 0; i < 1000; i++) {
    stmt.executeUpdate("INSERT INTO users (name, age) VALUES ('User" + i + "', 25);");
}

每条 INSERT 语句都会执行一次事务提交,导致 性能低下

优化方式(快)

使用 事务+批处理

try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db")) {
    conn.setAutoCommit(false);  // 关闭自动提交,使用事务

    try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)")) {
        for (int i = 0; i < 1000; i++) {
            pstmt.setString(1, "User" + i);
            pstmt.setInt(2, 25);
            pstmt.addBatch();  // 加入批处理
        }
        pstmt.executeBatch();  // 一次性提交批量插入
        conn.commit();
        System.out.println("批量插入完成!");
    } catch (SQLException e) {
        conn.rollback();  // 事务回滚
        System.out.println("插入失败: " + e.getMessage());
    }
}

为什么这样更快?

  • 减少磁盘 IO:事务提交一次,避免 每条 INSERT 都触发 COMMIT
  • 批处理:一次性执行 1000 条 INSERT,大幅减少 SQL 解析时间。

10.4. 适当使用 PRAGMA 指令

SQLite 允许通过 PRAGMA 进行 数据库优化

调整缓存大小

stmt.execute("PRAGMA cache_size = 10000;");  // 以页面为单位,每页 4KB

缓存可以减少磁盘 IO,提高查询速度

提升 INSERT 速度

stmt.execute("PRAGMA synchronous = OFF;");
stmt.execute("PRAGMA journal_mode = MEMORY;");
  • PRAGMA synchronous = OFF;:减少同步操作,提高写入速度。
  • PRAGMA journal_mode = MEMORY;:事务日志存储在内存中,加快写入速度。

10.5. 处理大数据查询

当表中数据量较大(百万级),查询可能会变慢。优化方法:

  1. 使用 LIMIT 分页查询
   String sql = "SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 100;";
  1. 避免 SELECT *,只查询需要的字段
   String sql = "SELECT name, age FROM users WHERE age > 20;";
  1. 使用 EXPLAIN QUERY PLAN 分析查询执行
   String sql = "EXPLAIN QUERY PLAN SELECT * FROM users WHERE name = 'Alice';";

10.6. 并发控制

SQLite 线程模式

SQLite 提供 三种线程模式

  • PRAGMA threading_mode = SINGLE; // 单线程
  • PRAGMA threading_mode = MULTI; // 多线程(默认)
  • PRAGMA threading_mode = SERIALIZED; // 串行模式,保证线程安全

对于 多线程写入,建议:

Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db?journal_mode=WAL");

这样可以支持多个线程并发写入。


10.7. SQLite 数据库备份

try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db");
     Statement stmt = conn.createStatement()) {
    stmt.executeUpdate("VACUUM;");
    System.out.println("数据库已优化!");
} catch (SQLException e) {
    System.out.println("优化失败: " + e.getMessage());
}

VACUUM;

  • 减少数据库文件大小
  • 提高查询性能

11. Java 与 SQLite 高级应用

本节继续深入讲解 Java 与 SQLite 的高级应用,涵盖 多线程数据库操作、ORM 框架(Hibernate)、Spring Boot 集成 SQLite、加密数据库、安全性优化 等内容。


11.1. SQLite 在多线程 Java 应用中的使用

默认情况下,SQLite 以单线程模式运行,如果多个线程同时操作数据库,可能会引发 database is locked 错误。为了解决这个问题,可以采取以下策略:

1. 设置 SQLite 线程模式

SQLite 提供三种线程模式:

  • SINGLE(默认):所有数据库连接必须在同一线程中。
  • MULTI:多个线程可以使用不同的连接,但同一连接不能被多个线程共享。
  • SERIALIZED(推荐):允许多个线程安全地访问同一连接。
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db?threadsafe=3")) {
    System.out.println("SQLite 线程模式设置为 SERIALIZED");
}

2. 使用 synchronized 保证线程安全

如果多个线程共享同一数据库连接,可以使用 synchronized 关键字:

public synchronized void insertUser(String name, int age) {
    try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)")) {
        pstmt.setString(1, name);
        pstmt.setInt(2, age);
        pstmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

3. 使用数据库连接池

SQLite 是轻量级数据库,不支持标准的数据库连接池(如 HikariCP)。不过可以使用 SQLite 连接池库 org.sqlite.SQLiteDataSource 来管理连接:

SQLiteDataSource dataSource = new SQLiteDataSource();
dataSource.setUrl("jdbc:sqlite:test.db");

try (Connection conn = dataSource.getConnection()) {
    System.out.println("使用 SQLite 连接池创建连接!");
}

11.2. 在 Java 中使用 Hibernate 操作 SQLite

Hibernate 是一个常见的 ORM(对象关系映射)框架,可以帮助我们使用 Java 类来操作 SQLite 数据库。

1. 添加 Hibernate 依赖

如果使用 Maven,在 pom.xml 添加:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.0.0.Final</version>
</dependency>

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.41.2.1</version>
</dependency>

2. 配置 Hibernate

创建 hibernate.cfg.xml

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.sqlite.JDBC</property>
        <property name="hibernate.connection.url">jdbc:sqlite:test.db</property>
        <property name="hibernate.dialect">org.hibernate.community.dialect.SQLiteDialect</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
    </session-factory>
</hibernate-configuration>

3. 定义实体类

创建 User.java

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private int age;

    // Getters 和 Setters
}

4. 使用 Hibernate 进行数据库操作

SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();

User user = new User();
user.setName("Alice");
user.setAge(25);
session.save(user);

tx.commit();
session.close();
factory.close();

Hibernate 自动管理 SQL 语句,并优化查询性能!


11.3. 在 Spring Boot 中使用 SQLite

如果你的 Java 项目是 Spring Boot,可以直接集成 SQLite!

1. 添加 SQLite 依赖

pom.xml 中:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.41.2.1</version>
</dependency>

2. 配置 application.properties

spring.datasource.url=jdbc:sqlite:test.db
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect

3. 创建 JPA 实体类

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int age;

    // Getters & Setters
}

4. 创建 Repository

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByName(String name);
}

5. 编写控制器

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
}

这样,我们的 Spring Boot 应用已经可以通过 REST API 操作 SQLite 数据库! 🚀


11.4. SQLite 数据库加密

SQLite 默认不支持加密,但可以使用 SQLCipher 对数据库进行加密。

1. 添加 SQLCipher 依赖

<dependency>
    <groupId>net.zetetic</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.41.2.1</version>
</dependency>

2. 设置数据库加密

try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db")) {
    try (Statement stmt = conn.createStatement()) {
        stmt.execute("PRAGMA key = 'my_secure_password';");
        System.out.println("数据库已加密!");
    }
}

11.5. SQLite 数据库的安全性优化

1. 使用 WAL 模式

stmt.execute("PRAGMA journal_mode=WAL;");

可以提高并发性能,同时减少数据库文件损坏的风险。

2. 只允许只读模式

Connection conn = DriverManager.getConnection("jdbc:sqlite:file:test.db?mode=ro");

防止误修改数据。

3. 禁用外部 SQL 访问

stmt.execute("PRAGMA foreign_keys=ON;");
stmt.execute("PRAGMA secure_delete=ON;");

总结

主题关键技术
多线程支持PRAGMA threads=3; + synchronized 关键字
使用 Hibernate配置 hibernate.cfg.xml,使用 SessionFactory
Spring Boot 集成spring-boot-starter-data-jpa + JpaRepository
数据库加密PRAGMA key = 'password';(SQLCipher)
安全优化PRAGMA journal_mode=WAL; + 只读模式

更多详细内容请关注其他相关文章。

发表回复 0

Your email address will not be published. Required fields are marked *