分享
 
 
 

在桌面应用中使用JAVA DB

王朝java/jsp·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

原文地址:http://java.sun.com/developer/technicalArticles/J2SE/Desktop/javadb/

原作者:John O'Conner

日期:2006-03

摘要

学习如何在Java桌面应用中部署基于Apache Derby的Java DB。该文章使用住址名册例子来向你演示一个嵌入式数据库Java DB是如何工作的。

正文

Sun Microsystems最近公布发行支持基于100%Java技术的开源数据库Java DB——Apache Derby数据库。Derby之前是以Cloudscape的名字存在并被人使用,它是由Cloudscape、Informix和IBM共同所有。后来,IBM把Derby产品源代码捐赠给Apache基金会作为一个开源项目。Sun、IBM 其他企业和个人作为Apache Derby社区的一部分也积极参与该关系数据库的开发。Java DB遍布在Sun许多产品中,包括Sun Java EnterPRise System和Sun Java System application Server。NetBeans集成开发环境(IDE) 5.0也支持Java DB。

Java DB是一个只有2MB的轻量级数据库,并可嵌入到Java技术的桌面应用中。目前桌面应用可以访问带有触发器、存储过程和支持SQL语句的强大数据库存储器,Java Database Connectivity(JDBC)和Java Platform, Enterprise Edition(Java EE,以前称为J2EE),都嵌入了同样的Java虚拟机(JVM)。(见脚注)

这篇文章描述了如何下载、安装、集成和在桌面应用中部署Java DB。住址名册这个例子将演示一个嵌入式数据库Java DB是如何工作的。

内容

-创建住址名册示例

-安装Java DB

-在NetBeans IDE 5.0里集成Java DB

-装载数据库驱动

-连接Java DB数据库

-创建数据库

-使用数据库

-发布你的应用程序

-概要

创建住址名册示例

住址名册示例使用Java DB来存储地址信息。这个示例存储名字、电话号码、email地址和邮政地址。它答应你建立一个新地址条目并可以存储、编辑和删除它们。这个应用程序在用户的主目录下名为.addressbook的子目录下创建它的数据库。这个数据库嵌入在应用程序中的,所以这里不需要设立和治理一个分开的服务器或系统。要发布这个嵌入式数据库应用程序,我们仅仅需要应用程序JAR文件和数据库类JAR文件。插图1展示该示例的用户界面(UI)。

插图1:住址名册使用嵌入式的数据库Java DB

住址名册的主框架窗口是AddressFrame类,它继续于Java Foundation Classes/Swing(JFC/Swing)JFrame。AddressFrame类是一个放置其他图形组件的容器,同时也担当控制和处理子组件产生的不同事件。这些子组件是JPanel的子类,每个都有不同的职责:

l AddressPanel显示地址记录。它也提供编辑存在的记录和创建新记录的UI。它包含显示Address对象的所有主要属性的文本域。

l AddressActionPanel设置该程序所需的按钮。AddressFrame必须处理这个面板所产生的事件。例如:当用户点击Save按钮,这个面板产生一个事件。AddressFrame监听并处理这个面板的所有重要事件。

l AddressListPanel设定一个带滚动条的列表来列出名册姓名,显示在AddressFrame的左边。这个列表控制一个ListEntry对象。ListEntry存储数据库记录的唯一标识。这个记录标识(ID)答应应用程序找到该记录的全部信息并显示在AddressPanel上。

该应用程序是用Data access Object(DAO)去分离数据库特定的代码。DAO封装了数据库connections和statements。一个DAO是一个有益的设计模式,它答应在应用程序和持久化存储机制之间的松耦合。应用程序的AddressDao类是一个DAO的例子。当AddressFrame编辑、保存或修改Address对象时,它总是使用一个AddressDao类的实例。虽然住址名册应用程序使用的是Java DB,你也可以改变它,并使用一个完全不同的数据库,仅仅需要修改这个类而已。

安装Java DB

获取Java DB最简单的方法就是从Sun Developer Network的Java DB站点去下载。二进制版本文件包提供你编写嵌入式数据库应用程序所需要的文件。当你下载完该文件后,你将得到Java DB目录结构,它包含下面一些子目录:

l Demo子目录有两个示例程序。一个例子显示如何创建一个普通的嵌入式应用。另一个例子则显示如何在客户端-服务器环境下使用Java DB。

l Frameworks子目录包含的功能有环境变量设置和建立和启动数据库。对于住址名册的示例,这个类是没有用的,因为我们的应用程序将是完全独立的。没有外部的功能被使用。

l Javadoc子目录包含API文档。这个目录非常有用的,当你配置你的IDE时,都要指明Java DB API javadoc的位置。

l Docs子目录包含关于Java DB产品的一些文档:安装、治理和参考指南。

l 最后,lib子目录包含Java DB库打包成的JAR文件。阅读Java DB文档可以找到不同的库。对于一个嵌入式数据库应用程序,我们们只需要derby.jar库文件。

安装Java DB只需要在你的应用程序环境变量里加入derby.jar文件。它是如此的简单。你可以在你的Solaris,linux,Windows里设置环境变量,或者在其他主环境里导入这个JAR文件,或者你可以在编译和运行的时候通过命令行参数导入这个文件。假如你使用ANT,住址名册示例的ANT脚本将告诉你在发布工程时如何导入这些JAR文件。另外,某些IDE,包括NetBeans IDE 5.0,答应你设置工程的环境变量。

在NetBeans IDE 5.0里集成Java DB

大多数IDE都提供了添加库文件到开发环境的方法。下面将指导你如何在NetBean IDE5.0里添加Java DB库文件:

1. 在Tools菜单里选择库治理器,如插图2。

插图2:库治理器答应你添加第三方库文件到你的工程里

2. 在库治理器窗口,创建一个名为JavaDBEmbedded的新库,如插图3。点击OK。

插图3:设置你工程需要的库的名字

3. 添加derby.jar文件到JavaDBEmbedded库,在库治理器窗口里点击Add JAR/Folder…。通过文件选择框选择derby.jar文件。如插图4。

插图4:添加derby.jar文件到JavaDBEmbedded库

4. 在同样的库治理器窗口的JavaDBEmbedded库里,选择Javadoc标签。从你安装的Java DB目录里添加javadoc子目录。现在,当你在NetBeans IDE工程里使用JavaDBEmbedded库时,就可用的Java DB API javadoc。

你现在可以使用工程的属性设置来为NetBeans IDE 5.0工程添加JavaDBEmbedded库文件。当你在IDE下编译,调试和运行该应用程序时,IDE将能找到所需要的derby.jar文件。

为了适用用户使用其他的IDE,我将derby.jar文件放在提供下载的地址名册工程的lib子目录里。也附带了只使用NetBeans IDE就能直接构建和运行该示例的ANT脚本。

装载数据库驱动

装载JDBC技术驱动启动数据库治理系统。Java DB的驱动来自于derby.jar文件,因此你不需要再下载任何东西。装载JDBC驱动通过引用Class.forName方法。嵌入式驱动名是org.apache.derby.jdbc.EmbeddedDriver,你也可以使用其他的JDBC驱动装载它。

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");

地址名册示例从配置属性文件里读取驱动名,并传递该名字到loadDriver方法。另外,之前提到的,地址名册封装所有的数据库功能到Data Access Object(DAO),core Java EE design pattern习惯访问数据从多样的来源。在Java SE应用程序中,就像地址名册一样,DAO模式工作得相当好。下面的代码片段表示AddressDao文件如何读取驱动名和装载驱动:

private Properties bProperties = null;

public AddressDao(String addressBookName) {

this.dbName = addressBookName;

setDBSystemDir();

dbProperties = loadDBProperties();

String driverName = dbProperties.getProperty("derby.driver");

loadDatabaseDriver(driverName);

...

}

private Properties loadDBProperties() {

InputStream dbPropInputStream = null;

dbPropInputStream =

AddressDao.class.getResourceAsStream("Configuration.properties");

dbProperties = new Properties();

try {

dbProperties.load(dbPropInputStream);

} catch (IOException ex) {

ex.printStackTrace();

}

return dbProperties;

}

private void loadDatabaseDriver(String driverName) {

// Load the Java DB driver.

try {

Class.forName(driverName);

} catch (ClassNotFoundException ex) {

ex.printStackTrace();

}

}

连接Java DB数据库

JDBC技术连接定义一个独立的数据库并答应你执行治理任务。任务包括启动,停止,复制,甚至删除数据库。驱动治理器提供所有数据库连接。从驱动治理器获得一个连接,倘若定义了URL字符串和一组属性值,将改变数据库连接的交互性。一个非常普通的方法是在连接时关联用户名和密码属性。

所有连接的URL都使用下面的格式:

jdbc:derby:<dbName>[propertyList]

dbName是定义一个独立数据库的URL。一个数据库可以有一个或多个位置:在当前工作目录里,在classpath里,在JAR文件里,在一个独有的Java DB数据库主目录里,或者是你的文件系统里的绝对位置。治理数据库位置的最简单方法是在你嵌入式环境里设置derby.system.home系统属性。这个属性告诉Java DB里所有数据库的默认主位置。通过设置这些属性,地址名册示例确保Java DB总是能找到正确的应用程序数据库。应用程序数据库名字为DefaultAddressBook,它将存在于derby.system.home属性所指示的目录里。连接该数据库的URL应该像这样:

jdbc:derby:DefaultAddressBook

可选值propertyList是一组属性,你可以传递给数据库系统。你可以传递属性到Java DB系统,要么是URL它本身,或是是分开的属性对象。假如属性是URL的一部分,应该用分号来隔开每个属性值。最常用的属性是:

l create=true

l databaseName=nameOfDatabase

l user=username

l passWord=userPassword

l shutdown=true

要连接DefaultAddressBook数据库,该示例必须首先设置derby.system.home系统属性。该示例使用的是用户主目录的.addressbook子目录。使用System类去找到用户的主目录。然后使用该类来设置derby.system.home属性:

private void setDBSystemDir() {

// Decide on the db system Directory: <userhome>/.addressbook/

String userHomeDir = System.getProperty("user.home", ".");

String systemDir = userHomeDir + "/.addressbook";

// Set the db system directory.

System.setProperty("derby.system.home", systemDir);

}

一旦应用程序有了明确的指示,所有的数据库都将存在,它可以获得一个数据库连接。在这个例子里,注重,我添加了连接属性到数据库URL。

Connection dbConnection = null;

String strUrl = "jdbc:derby:DefaultAddressBook;user=dbuser;password=dbuserpwd";

try {

dbConnection = DriverManager.getConnection(strUrl);

} catch (SQLException sqle) {

sqle.printStackTrace();

}

或者,你可以把这些属性放在Properties对象里。当获取连接时,使用这个Properties对象做参数:

Connection dbConnection = null;

String strUrl = "jdbc:derby:DefaultAddressBook";

Properties props = new Properties();

props.put("user", "dbuser");

props.put("password", "dbuserpwd");

try {

dbConnection = DriverManager.getConnection(strUrl, props);

} catch(SQLException sqle) {

sqle.printStackTrace();

}

创建数据库

地址名册示例应用程序没有现成的数据库。换句话说,当该应用程序启动时,必须创建数据库。在这个应用程序中使用嵌入式数据库最大的好处是不需要用户关心数据库设置的细节。应用程序可以控制数据库存在的地方,存在那些表和如何进行处理。

地址名册创建了一个名为DefaultAddressBook的数据库,在用户主目录的一个子目录里,它不会告诉用户任何附加的信息。当获得一个数据库连接后,通过使用create=true属性,你可以在Java DB里创建一个新的数据库。由于我们的数据库将使用DefaultAddressBook数据库,我们首先应该创建这个数据库。假设我们在之前讨论时已经设置了derby.system.home属性值,应用程序创建数据库和连接如下:

Connection dbConnection = null;

String strUrl = "jdbc:derby:DefaultAddressBook;create=true";

try {

dbConnection = DriverManager.getConnection(strUrl);

} catch (SQLException ex) {

ex.printStackTrace();

}

因为create=true属性值被包含在里面,Java DB首先会尝试创建数据库文件。创建数据库并不是实际创建任何应用表。然而,你应该能在你的主目录下找到一个名为.addressbook/DefaultAddressBook的子目录。

在数据库被创建后,应用程序将创建表。该示例仅使用一个ADDRESS表在默认应用程序APP计划。下面是创建ADDRESS表的SQL代码:

CREATE table APP.ADDRESS (

ID INTEGER NOT NULL

PRIMARY KEY GENERATED ALWAYS AS IDENTITY

(START WITH 1, INCREMENT BY 1),

LASTNAME VARCHAR(30),

FIRSTNAME VARCHAR(30),

MIDDLENAME VARCHAR(30),

PHONE VARCHAR(20),

EMAIL VARCHAR(30),

ADDRESS1 VARCHAR(30),

ADDRESS2 VARCHAR(30),

CITY VARCHAR(30),

STATE VARCHAR(30),

POSTALCODE VARCHAR(20),

COUNTRY VARCHAR(30) )

每个记录有一个记录标识或ID域。Java DB为每条新记录产生这些值,它将添加到数据。在每条地址记录的ID域是一个要害值。

其他的地址记录域都是不同长度的varchar类型。例如,LASTNAME域能容纳最大30个varchar字符。Varchar类型等价于UTF-16 Java char类型。

Java技术编码使用下面的代码用上面的SQL语句去创建ADDRESS表。dbConnection是与先前提到的代码相同的。我们把它传递到createTable方法中,创建一个新的Statement,并在新建的数据库里调用execute方法运行SQL代码。strCreateAddressTable实例变量保存SQL语句文本。

private boolean createTables(Connection dbConnection) {

boolean bCreatedTables = false;

Statement statement = null;

try {

statement = dbConnection.createStatement();

statement.execute(strCreateAddressTable);

bCreatedTables = true;

} catch (SQLException ex) {

ex.printStackTrace();

}

return bCreatedTables;

}

现在,数据库和ADDRESS表都存在于主目录下名为.addressbook/DefaultAddressBook子目录里。虽然你可以浏览这个子目录,避免修改任何文件。假如你直接编辑或删除这些数据库文件,你就破坏了你数据库的完整性。

使用数据库

一旦数据库和它的表被创建,你的应用程序可以创建一个新的连接和声明去增加,编辑,删除,或获得记录。在地址名册中,这些响应是由AddressActionPanel里的按钮所控制。插图5显示了这几个可选项。

l New.创建一个新的地址记录

l Delete.删除当前显示的地址记录

l Edit.编辑当前的地址记录

l Save.保存新的和编辑过的地址记录

l Cancel.取消任何编辑或任何尝试新建的记录

插图5:地址名册有几个按钮与记录交互

应用程序的主窗口是AddressFrame,它同时担当控制和显示。它用AddressActionPanel注册它自己去接收通知,当用户点击响应区的按钮时。

New按钮清除地址条目面版,并答应用户去编辑所有文本框。这里没有使用SQL命令,不过UI应该答应你登录一个新地址。

Delete按钮尝试删除当前选种的地址记录。AddressFrame从AddressPanel获取当前选择的Address标识,并使用AddressDao删除该记录。该面板调用它自己的deleteAddress方法。该方法用正确的ID做参数调用DAO的deleteRecord方法。在删除数据库记录后,应用程序必须删除AddressListPanel的ListEntry。

private void deleteAddress() {

int id = addressPanel.getId();

if (id != -1) {

db.deleteRecord(id);

int selectedIndex = addressListPanel.deleteSelectedEntry();

...

}

...

}

在AddressDao里,deleteRecord方式实际上是从数据库删除一条记录。当第一次创建数据库连接时,AddressDao就创建了一个PreparedStatement。

stmtDeleteAddress = dbConnection.prepareStatement(

"DELETE FROM APP.ADDRESS " +

"WHERE ID = ?");

PreparedStatement可以使用多次,每次只需要一个参数就能确定删除哪条记录。在设置了ID参数后,deleteRecord方法执行Update。

public boolean deleteRecord(int id) {

boolean bDeleted = false;

try {

stmtDeleteAddress.clearParameters();

stmtDeleteAddress.setInt(1, id);

stmtDeleteAddress.executeUpdate();

bDeleted = true;

} catch (SQLException sqle) {

sqle.printStackTrace();

}

return bDeleted;

}

Edit按钮答应用户在AddressPanel里编辑当前选择的Address记录。在这个例子中,你可以修改记录的名字,城市或电话号码。

Save按钮要么是在AddressPanel里创建和编辑新的Address,要么就尝试编辑更新存在的记录。假如用户是编辑一个记录。Save按钮将使用新的信息来更新记录。假如用户是创建一个新的记录,Save按钮将插入一个新的记录到数据库。新的还没有被保存。这时,它的ID域仍然设置的是默认值-1。一旦你保存这条记录,这个值是自动生成的,并成为该记录的唯一标识。

下面是AddresFrame里的代码是通过调用DAO的editRecord或saveRecord方法来保存编辑后或新建的地址记录。当然,当你创建一条新记录,应用程序也必须更新AddressListPanel。

private void saveAddress() {

if (addressPanel.isEditable()) {

Address address = addressPanel.getAddress();

int id = address.getId();

if (id == -1) {

id = db.saveRecord(address);

address.setId(id);

String lname = address.getLastName();

String fname = address.getFirstName();

String mname = address.getMiddleName();

ListEntry entry = new ListEntry(lname, fname, mname, id);

addressListPanel.addListEntry(entry);

} else {

db.editRecord(address);

}

addressPanel.setEditable(false);

}

}

DAO的editRecord方法必须更新Address记录里更改的域。由于该应用程序示例不能分辨修改和未修改的域,它就更新了记录的所有域。下面是PreparedStatement对象和editRecord方法:

stmtUpdateExistingRecord = dbConnection.prepareStatement(

"UPDATE APP.ADDRESS " +

"SET LASTNAME = ?, " +

" FIRSTNAME = ?, " +

" MIDDLENAME = ?, " +

" PHONE = ?, " +

" EMAIL = ?, " +

" ADDRESS1 = ?, " +

" ADDRESS2 = ?, " +

" CITY = ?, " +

" STATE = ?, " +

" POSTALCODE = ?, " +

" COUNTRY = ? " +

"WHERE ID = ?");

...

public boolean editRecord(Address record) {

boolean bEdited = false;

try {

stmtUpdateExistingRecord.clearParameters();

stmtUpdateExistingRecord.setString(1, record.getLastName());

stmtUpdateExistingRecord.setString(2, record.getFirstName());

stmtUpdateExistingRecord.setString(3, record.getMiddleName());

stmtUpdateExistingRecord.setString(4, record.getPhone());

stmtUpdateExistingRecord.setString(5, record.getEmail());

stmtUpdateExistingRecord.setString(6, record.getAddress1());

stmtUpdateExistingRecord.setString(7, record.getAddress2());

stmtUpdateExistingRecord.setString(8, record.getCity());

stmtUpdateExistingRecord.setString(9, record.getState());

stmtUpdateExistingRecord.setString(10, record.getPostalCode());

stmtUpdateExistingRecord.setString(11, record.getCountry());

stmtUpdateExistingRecord.setInt(12, record.getId());

stmtUpdateExistingRecord.executeUpdate();

bEdited = true;

} catch(SQLException sqle) {

sqle.printStackTrace();

}

return bEdited;

}

保存一个新的Address数据库记录,该记录有一个新的要害值或记录标识。当我们创建PreparedStatement时,我们可以告诉数据库我们想要知道产生的值。在插入数据后,我们可以通过该值返回一个ResultSet。saveRecord方法返回新创建记录的要害值。

stmtSaveNewRecord = dbConnection.prepareStatement(

"INSERT INTO APP.ADDRESS " +

" (LASTNAME, FIRSTNAME, MIDDLENAME, " +

" PHONE, EMAIL, ADDRESS1, ADDRESS2, " +

" CITY, STATE, POSTALCODE, COUNTRY) " +

"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",

Statement.RETURN_GENERATED_KEYS);

...

public int saveRecord(Address record) {

int id = -1;

try {

stmtSaveNewRecord.clearParameters();

stmtSaveNewRecord.setString(1, record.getLastName());

stmtSaveNewRecord.setString(2, record.getFirstName());

stmtSaveNewRecord.setString(3, record.getMiddleName());

stmtSaveNewRecord.setString(4, record.getPhone());

stmtSaveNewRecord.setString(5, record.getEmail());

stmtSaveNewRecord.setString(6, record.getAddress1());

stmtSaveNewRecord.setString(7, record.getAddress2());

stmtSaveNewRecord.setString(8, record.getCity());

stmtSaveNewRecord.setString(9, record.getState());

stmtSaveNewRecord.setString(10, record.getPostalCode());

stmtSaveNewRecord.setString(11, record.getCountry());

int rowCount = stmtSaveNewRecord.executeUpdate();

ResultSet results = stmtSaveNewRecord.getGeneratedKeys();

if (results.next()) {

id = results.getInt(1);

}

} catch(SQLException sqle) {

sqle.printStackTrace();

}

return id;

}

发布你的应用程序

现在你已经写好了你的应用程序,你必须发布它给用户。Java技术应用程序可以使用多种发布策略。包括Java Web Start软件,Applet和独立的JAR文件。我以独立的JAR文件的形式发布了该应用程序。

ANT的构建文件build.xml,创建AddressBook.jar文件到dist目录。它也放置数据库JAR文件到lib子目录。该应用程序最终发布的结构如下:

AddressBook.jar

lib/derby.jar

在很多情况下,应用程序使用例如derby.jar这样的第三方类库时,都需要一个外部的执行脚本。这个脚本通常设置第三方JAR文件的classpath和执行该应用程序的JAR文件。这个方法很麻烦。然而,由于它需要多个脚本,具有代表的一个是支持主机操作系统。例如,如何支持该程序发布到Windows,Solaris和Linux平台,我宁愿为Windows平台创建一个run.bat批处理文件,为Solaris或Linux平台创建一个run.csh脚本。换言之,我们可以避免这些笨拙的执行脚本。

假如在AddressBook.jar的manifest文件里包含了classpath信息,你可以通过简单的命令行操作执行AddressBook.jar应用程序。在大多数平台,你可以在图形窗口界面中双击该JAR文件来运行该应用程序。在命令行下,你可以使用下面简单的执行命令:

java -jar AddressBook.jar

这样简单的发布和执行方案可以创建manifest.mf文件来实现,并成为AddressBook.jar文件的一部分。你可以写入信息到manifest文件里,并告诉Java编程语言解释器那个类包含了main方法和那些JAR文件应该被认为是classpath的一部分。下面的manifest文件做到了以上两点,当构建AddressBook.jar文件时,我们可以把它放在JAR文件里。

Manifest-Version: 1.0

Main-Class: com.sun.demo.addressbook.AddressFrame

Class-Path: lib/derby.jar

一旦你构建程序产生先前所显示的发布结构,你可以简单的发布成为一个Zip文件。使用者可以轻松的解压该文件到任何位置,并能运行AddressBook.jar文件。AddressBook.jar文件将包含之前提到的manifest文件和告诉运行时环境哪个JAR文件应该在classpath里。当然,因为Java DB是嵌入到这个程序里的,它应该能找到lib/derby.jar并能正确运行。

概要

用Java DB工作是简单和有趣的。Java DB让你花最小的代价去创建和发布一个嵌入式数据库。仅仅需要记住几点技巧就能使你成功的使用Java DB工作了:

1. 请将derby.jar文件放在你开发环境的classpath里,以便Java技术编译器和运行时环境能找到该库并能编译和运行应用程序。

2. 设置derby.system.home系统属性去告诉Java DB在那里能找到数据库。你可以通过程序代码或命令行来设置该属性。

3. 创建一个构建程序,请确保将derby.jar文件放在你工程目录的lib子目录下。

4. 通过设置你的应用程序的JAR的manifest文件里的Class-Path属性,添加derby.jar到你的应用程序classpath里。

脚注

术语”Java Virtual Machine”和”JVM”意思是Java平台的虚拟机。

更多信息

l 下载住址名册示例

l Java DB下载站点

l 在客户端-服务器环境中使用Derby

l 下载NetBeans IDE

l Apache Derby项目

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有