在 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
条件中的列。 - 避免过多索引,索引会影响
INSERT
和UPDATE
的性能。 - 使用
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. 处理大数据查询
当表中数据量较大(百万级),查询可能会变慢。优化方法:
- 使用
LIMIT
分页查询
String sql = "SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 100;";
- 避免
SELECT *
,只查询需要的字段
String sql = "SELECT name, age FROM users WHERE age > 20;";
- 使用
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; + 只读模式 |
更多详细内容请关注其他相关文章。