JavaFX 集成 Sqlite 和 Hibernate 开发爬虫应用
目录 [隐藏]
前言:
在开发 JavaFX 应用总是避免不了数据存储的,如果仅仅是一个简单的配置数据,那么不需要数据库即可实现,那么如果要面对几十万等大数据量的持久化存储,那免不了要和数据库和JDBC框架打交道了。
数据库该怎么选呢? 首先考虑我比较熟的 MySql,可是要使用MySql,你就必须要去官网下载MySql的安装包,还要进行账号密码等配置,如果这软件是面向大众的,用户要使用总不能还要先装数据库,再看半天安装教程吧?
这不行,那么我之前有接触过两个嵌入式数据库,一个是H2,一个就是开发Android 时接触的Sqlite。
H2 我事先考察了一下,觉得资料并不是很多,远没有 Sqlite 使用广泛,而且 Sqlite 是 Android 官方内置的数据库,我还去看了 Sqlite 最大数据存储等测试文章,亿级的数据量下还能保持性能,这才放心使用。
界面



Maven 环境
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jfoenix</groupId>
<artifactId>jfoenix</artifactId>
<version>8.0.8</version>
</dependency>
<!--sqlite 版本3.7.2 -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>4.1.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.datafx/flow -->
<dependency>
<groupId>io.datafx</groupId>
<artifactId>flow</artifactId>
<version>8.0.1</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>8.40.14</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.inamik.text.tables/inamik-text-tables -->
<dependency>
<groupId>com.github.inamik.text.tables</groupId>
<artifactId>inamik-text-tables</artifactId>
<version>0.8</version>
</dependency>
项目结构

整合 Hibernate
Hibernate 并不支持 Sqlite,但只是缺少一个数据库方言代码而已,这个在网上有很多,copy 一份在hibernate配置文件中引入就可以了。
SQLiteDialect.java 数据库方言代码
package util;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.type.StandardBasicTypes;
import java.sql.Types;
public class SQLiteDialect extends Dialect {
public SQLiteDialect() {
super();
registerColumnType(Types.BIT, "integer");
registerColumnType(Types.TINYINT, "tinyint");
registerColumnType(Types.SMALLINT, "smallint");
registerColumnType(Types.INTEGER, "integer");
registerColumnType(Types.BIGINT, "bigint");
registerColumnType(Types.FLOAT, "float");
registerColumnType(Types.REAL, "real");
registerColumnType(Types.DOUBLE, "double");
registerColumnType(Types.NUMERIC, "numeric");
registerColumnType(Types.DECIMAL, "decimal");
registerColumnType(Types.CHAR, "char");
registerColumnType(Types.VARCHAR, "varchar");
registerColumnType(Types.LONGVARCHAR, "longvarchar");
registerColumnType(Types.DATE, "date");
registerColumnType(Types.TIME, "time");
registerColumnType(Types.TIMESTAMP, "timestamp");
registerColumnType(Types.BINARY, "blob");
registerColumnType(Types.VARBINARY, "blob");
registerColumnType(Types.LONGVARBINARY, "blob");
// registerColumnType(Types.NULL, "null");
registerColumnType(Types.BLOB, "blob");
registerColumnType(Types.CLOB, "clob");
registerColumnType(Types.BOOLEAN, "integer");
registerFunction("concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "", "||", ""));
registerFunction("mod", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "?1 % ?2"));
registerFunction("substr", new StandardSQLFunction("substr", StandardBasicTypes.STRING));
registerFunction("substring", new StandardSQLFunction("substr", StandardBasicTypes.STRING));
}
public boolean supportsIdentityColumns() {
return true;
}
public boolean hasDataTypeInIdentityColumn() {
return false;
}
public String getIdentityColumnString() {
return "integer";
}
public String getIdentitySelectString() {
return "select last_insert_rowid()";
}
public boolean supportsLimit() {
return true;
}
public String getLimitString(String query, boolean hasOffset) {
return new StringBuffer(query.length() + 20).append(query).append(hasOffset ? " limit ? offset ?" : " limit ?")
.toString();
}
public boolean supportsTemporaryTables() {
return true;
}
public String getCreateTemporaryTableString() {
return "create temporary table if not exists";
}
public boolean dropTemporaryTableAfterUse() {
return false;
}
public boolean supportsCurrentTimestampSelection() {
return true;
}
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
public String getCurrentTimestampSelectString() {
return "select current_timestamp";
}
public boolean supportsUnionAll() {
return true;
}
public boolean hasAlterTable() {
return false;
}
public boolean dropConstraints() {
return false;
}
public String getAddColumnString() {
return "add column";
}
public String getForUpdateString() {
return "";
}
public boolean supportsOuterJoinForUpdate() {
return false;
}
public String getDropForeignKeyString() {
throw new UnsupportedOperationException("No drop foreign key syntax supported by SQLiteDialect");
}
public String getAddForeignKeyConstraintString(String constraintName, String[] foreignKey, String referencedTable,
String[] primaryKey, boolean referencesPrimaryKey) {
throw new UnsupportedOperationException("No add foreign key syntax supported by SQLiteDialect");
}
public String getAddPrimaryKeyConstraintString(String constraintName) {
throw new UnsupportedOperationException("No add primary key syntax supported by SQLiteDialect");
}
public boolean supportsIfExistsBeforeTableName() {
return true;
}
public boolean supportsCascadeDelete() {
return false;
}
@Override
public boolean bindLimitParametersInReverseOrder() {
return true;
}
}
hibernate.cfg.xml Hibernate配置文件
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">util.SQLiteDialect</property> <!-- 数据库方言 -->
<property name="hibernate.connection.driver_class">org.sqlite.JDBC</property><!-- 引用jdbc包 -->
<property name="hibernate.connection.url">jdbc:sqlite:D:\eclipse_workspace\Letv\src\main\resources\db\letv.db</property> <!-- 数据库链接 -->
<!-- <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!– 数据库方言 –>-->
<!-- <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property><!– 引用jdbc包 –>-->
<!-- <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/bbs?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT&nullCatalogMeansCurrent=true</property> <!– 数据库链接 –>-->
<!-- <property name="hibernate.connection.username">root</property>-->
<!-- <property name="hibernate.connection.password">1234</property>-->
<property name="hibernate.format_sql">true</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="db/LetvConfigEntity.hbm.xml"/>
<mapping resource="db/LetvCookieEntity.hbm.xml"/>
<mapping resource="db/LetvLinkEntity.hbm.xml"/>
<mapping resource="db/LetvUserEntity.hbm.xml"/>
</session-factory>
</hibernate-configuration>
这里的 :<property name=”hibernate.connection.url”>jdbc:sqlite:D:\eclipse_workspace\Letv\src\main\resources\db\letv.db</property>
是绝对路径,这个是用 idea 自动生成给我改了,可以使用相对路径在项目根目录下创建数据库文件,而 url 只需要 : String url = “jdbc:sqlite:src/main/resources/db/letv.db”;
这样写就可以。
项目初始化连接数据库自动建表:
数据库那肯定不能没有表,而表又不可能让用户去建,所以只能让程序代劳,并不难,用 Navicat 打开数据库建好表,导出 sql 文件,将其中的建表语句提取出来,在项目初次加载时,找到 sqlite 的 db 后缀的数据库文件,如果没有,那么创建,连接数据库,执行建表语句。
public class SqliteUtil {
private static Connection connection;
public synchronized static Connection getConnection() throws SQLException {
//如果 当前练
if (connection == null) {
try {
String driverClass = "org.sqlite.JDBC";
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = "jdbc:sqlite:src/main/resources/db/letv.db";
return connection = DriverManager.getConnection(url);
} else {
return connection;
}
}
public static void close() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
} else {
throw new NullPointerException("连接未开启!");
}
}
这段代码中,getConnection() 方法调用后会连接 Sqlite 数据库,如果没有,则会创建。
public static final String DB_USER = "create table letv_user (" +
" letv_user_id integer(11) not null," +
" letv_user_uid text(11)," +
" letv_user_link text(40)," +
" primary key (letv_user_id)" +
");";
public static void createDatabases() throws SQLException, IOException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute(DB_CONFIG);
statement.execute(DB_COOKIE);
statement.execute(DB_LINK);
statement.execute(DB_USER);
close();
}
定义建表语句,调用 createDatabases() 则会执行建表语句创建表。
程序初次运行创建数据库和表
if (this.getClass().getResource("db/letv.db") == null) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Loading.fxml"));
AnchorPane pane = loader.load();
Scene scene = new Scene(pane, 500, 300);
PleaseProvideController loadController = loader.getController();
loadController.setInfo("正在创建数据库……");
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setAlwaysOnTop(true);
primaryStage.getIcons().addAll(new Image("file:/resource/logo/logo.ico"));
primaryStage.show();
Platform.runLater(() -> {
try {
SqliteUtil.createDatabases();
logger.info("数据库创建成功!");
primaryStage.close();
start(new Stage());
} catch (SQLException | IOException e) {
e.printStackTrace();
}
});
}
创建数据库需要一些时间,这个时候可以给一个 Loading 界面:

经过测试发现创建数据库和表时间并不长,所以这一步可以省略,当然如果表多那就看情况了。
JFoenix 界面开发
JFoenix 的界面非常好看,Google Material 的设计风格,案例:
这个 UI 库是开源的,非常美观,提供的控件也很丰富,只是文档感觉不是很好,但好在可以再官方提供的 Demo 案例查看控件的使用。
Github 地址 :
https://github.com/jfoenixadmin/JFoenix
官方文档 :
http://www.jfoenix.com/documentation.html
JFoenix 表格 TreeTable
官方案例:

如果觉得 JFoenix 的表格实现代码要比原生的简单,那你就错了,代码量依旧比较大,而且如果需要对表的列绑定字段,字段不是只读,就是如果你需要表的字段可以被编辑操作,那么相应的绑定的字段类型必须是 JavaFX 提供的 Property 类型,JavaFX 为这种类型提供了绑定方法,但是如果是使用这种类型去结合 Hibernate 字段映射,报错没跑了。
所以,我只能将用户映射表的实体类和绑定表的类分开,分为两个类,一个字段类型是原生的,另一个字段类型是 Property 类型。
public class LetvCookieTable extends RecursiveTreeObject<LetvCookieTable> {
public long cookieId;
public StringProperty cookieKey;
public StringProperty cookieValue;
public LetvCookieTable(long cookieId,String cookieKey, String cookieValue) {
this.cookieId = cookieId;
this.cookieKey = new SimpleStringProperty(cookieKey);
this.cookieValue = new SimpleStringProperty(cookieValue);
}
这个就是用来绑定表的实体类,再表格界面加载的时候,查询返回实体类结果集,接着将实体类转换成 Property 类型的类添加到 ObservableList 中。
字段绑定
//column
JFXTreeTableColumn<LetvCookieTable, String> key = new JFXTreeTableColumn<>("Key");
key.setCellValueFactory((TreeTableColumn.CellDataFeatures<LetvCookieTable, String> param) -> {
if (key.validateValue(param)) {
return param.getValue().getValue().cookieKey;
} else {
return key.getComputedValue(param);
}
});
JFXTreeTableColumn<LetvCookieTable, String> value = new JFXTreeTableColumn<>("Value");
value.setCellValueFactory((TreeTableColumn.CellDataFeatures<LetvCookieTable, String> param) -> {
if (value.validateValue(param)) {
return param.getValue().getValue().cookieValue;
} else {
return value.getComputedValue(param);
}
});
TreeTable 绑定删除按钮
现在需要一个删除的列,提供删除按钮,点击后删除这一行的数据。
代码和 TableView 大体上是一样的,但在取值上有点小差异。
JFXTreeTableColumn<LetvCookieTable, String> options = new JFXTreeTableColumn<>("options");
options.setCellFactory(new Callback<TreeTableColumn<LetvCookieTable, String>, TreeTableCell<LetvCookieTable, String>>() {
@Override
public TreeTableCell<LetvCookieTable, String> call(TreeTableColumn<LetvCookieTable, String> param) {
JFXButton button = new JFXButton();
button.setText("删除");
return new TreeTableCell<LetvCookieTable, String>() {
JFXButton delBtn = button;
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
delBtn.setOnMouseClicked(event -> {
Transaction transaction = session.beginTransaction();
logger.info("删除:" + getIndex());
LetvCookieTable table = getTreeTableView().getTreeItem(getIndex()).getValue();
session.doWork(connection -> {
Statement st;
logger.info("id:" + table.cookieId);
String sql = "delete from letv_cookie where cookie_id = " + table.cookieId;
st = connection.createStatement();
st.executeUpdate(sql);
st.close();
});
NotificationUtil.notification("信息", "删除成功", "info");
transaction.commit();
observableCookie.remove(getIndex());
});
setGraphic(button);
setText(null);
}
}
};
}
});
JavaFX 获取当前行 :
getTableView().getItems().get(getIndex())
JFoenix :
getTreeTableView().getTreeItem(getIndex()).getValue()
TreeTable 可编辑
key.setCellFactory((TreeTableColumn<LetvCookieTable, String> param) -> new GenericEditableTreeTableCell<>(
new TextFieldEditorBuilder()));
key.setOnEditCommit((TreeTableColumn.CellEditEvent<LetvCookieTable, String> t) -> {
LetvCookieTable table = t.getTreeTableView().getTreeItem(t.getTreeTablePosition()
.getRow())
.getValue();
table.cookieKey.set(t.getNewValue());
Transaction transaction = session.beginTransaction();
Query updateLink = session.createQuery("update db.LetvCookieEntity set cookieKey = :newVal where cookieId=" + table.cookieId);
updateLink.setParameter("newVal", t.getNewValue());
updateLink.executeUpdate();
transaction.commit();
session.clear();
NotificationUtil.notification("信息","更新成功!","info");
});
未完待续 ……
JavaFX 集成 Sqlite 和 Hibernate 开发爬虫应用的更多相关文章
- hibernate开发(1)
1 什么是ORM OR-mapping 是 object relational mapping 对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.解决了不同数据库s ...
- 持久化框架Hibernate 开发实例(一)
1 Hibernate简介 Hibernate框架是一个非常流行的持久化框架,其中在web开发中占据了非常重要的地位, Hibernate作为Web应用的底层,实现了对数据库操作的封装.HIberna ...
- nodejs集成sqlite
正在物色node上面的轻量级嵌入式数据库,作为嵌入式数据库的代表,sqlite无疑是个理想的选择方案.npm上集成sqlite的库主要有两个——sqlite3和realm. realm是一个理想的选择 ...
- Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建
Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...
- 手把手在MyEclipse中搭建Hibernate开发环境
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/53414303冷血之心的博客) 在MyEclipse中如何搭建Hib ...
- java开发爬虫Deno
java开发爬虫Deno 身为一个程序员不会两三手爬虫怎么能在行业里立足啊,这是开发中自己写的一个java爬虫的Demo,供大家参考. java爬虫的开发依赖于jsoup.jar 直接上代码 publ ...
- Unity3D游戏开发之SQLite让数据库开发更简单
各位朋友大家好.欢迎大家关注我的博客,我是秦元培,我是博客地址是http://blog.csdn.net/qinyuanpei.在经历了一段时间的忙碌后,博主最终有时间来研究新的东西啦,今天博客向和大 ...
- Hibernate开发环境搭建
一.下载Hibernate包的下载 官网地址:http://hibernate.org/orm/ 下载版本:hibernate-release-4.3.11.Final 二.Hibernate jar ...
- BT网站--Python开发爬虫代替.NET
BT网站-奥修磁力-Python开发爬虫代替.NET写的爬虫,主要演示访问速度和在一千万左右的HASH记录中索引效率. IBMID 磁力下载- WWW.IBMID.COM 现在用的是Python + ...
随机推荐
- Python笔记【3】_元组学习
#!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 ''' tuple:以圆括号“()”括起来,以“ ...
- LBXF时间管理法2.0
前言 LBXF是柳比歇夫的中文拼音缩写,下文中会着重介绍柳比歇夫 时间管理法主要帮助我们提高生产性时间,以及提高生产效率.我会按照柳比歇夫.执行步骤.时间管理工具Toggle三个模块来讲解LBXF时间 ...
- 我是这样理解--SVM,不需要繁杂公式的那种!(附代码)
1. 讲讲SVM 1.1 一个关于SVM的童话故事 支持向量机(Support Vector Machine,SVM)是众多监督学习方法中十分出色的一种,几乎所有讲述经典机器学习方法的教材都会介绍.关 ...
- Cow Exhibition POJ - 2184
题目地址:https://vjudge.net/problem/POJ-2184 下面的解释是从一个大佬那搬来的,讲的很清楚题意:给定一些奶牛,每个牛有s和f两个属性值,有正有负,要求选出一些牛,使得 ...
- 牛逼了,教你用九种语言在JVM上输出HelloWorld
我们在<深入分析Java的编译原理>中提到过,为了让Java语言具有良好的跨平台能力,Java独具匠心的提供了一种可以在所有平台上都能使用的一种中间代码——字节码(ByteCode). 有 ...
- android_sdcard读写(三)
这次来个稍微复杂点的. package cn.com.sxp;import android.app.Activity;import android.app.ProgressDialog;import ...
- OpenCV多版本切换和配置--opencv 安装与卸载、添加 opencv_contrib modules 以及 OpenCv 多版本切换
1. 查看安装Opencv的版本.以及libs和cflags $ pkg-config --modversion opencv $ pkg-config --cflags opencv // 编译链接 ...
- CF39D Cubical Planet-C++
银河系中没有你找不到的东西!有一颗形状为立方体的的行星正在绕着一颗形状为二十面体的恒星运转.现在我们让这颗行星的两个在同一条体对角线上的顶点置于(0,0,0)和(1,1,1)上.有两只苍蝇住在行星上. ...
- 「PowerBI」Tabular Editor 一个对中文世界很严重的bug即将修复完成
之前介绍过Tabular Editor这款开源工具,对PowerBI建模来说,非常好用,可以极大的增强自动化水平. 详细可查看此文章: 「PowerBI相关」一款极其优秀的DAX建模工具Tabular ...
- 个人永久性免费-Excel催化剂功能第45波-逻辑判断函数增强
自定义函数的最大的作用是可以按需定制,在Excel的原生函数不提供的场景时,传统方法需要使用大量的嵌套函数去实现,实在太累,今天Excel催化剂再次送上一波绝对十分常用的函数逻辑判断类函数给大家使用! ...