在我的小型独立Java应用程序中,我想存储信息。
我的要求:
因此,我想使用jaxb将所有信息存储在文件系统中的简单 XML文件中。我的示例应用程序看起来像这样(将所有代码复制到名为Application.java
的文件中并编译,没有其他要求!):
@XmlRootElement
class DataStorage {
String emailAddress;
List<String> familyMembers;
// List<Address> addresses;
}
public class Application {
private static JAXBContext jc;
private static File storageLocation = new File("data.xml");
public static void main(String[] args) throws Exception {
jc = JAXBContext.newInstance(DataStorage.class);
DataStorage dataStorage = load();
// the main application will be executed here
// data manipulation like this:
dataStorage.emailAddress = "me@example.com";
dataStorage.familyMembers.add("Mike");
save(dataStorage);
}
protected static DataStorage load() throws JAXBException {
if (storageLocation.exists()) {
StreamSource source = new StreamSource(storageLocation);
return (DataStorage) jc.createUnmarshaller().unmarshal(source);
}
return new DataStorage();
}
protected static void save(DataStorage dataStorage) throws JAXBException {
jc.createMarshaller().marshal(dataStorage, storageLocation);
}
}
我如何克服这些缺点?
答案 0 :(得分:7)
查看您的要求:
我认为基于XML的文件系统是不够的。 如果你认为一个适当的关系数据库是一个过度杀手,你仍然可以选择H2 db。这是一个超轻量级的数据库,它可以解决上述所有这些问题(即使不完美,但是肯定比手写的XML数据库好得多,并且仍然很容易设置和维护。
您可以将其配置为将更改保留到磁盘,可以配置为作为独立服务器运行并接受多个连接,也可以作为嵌入式模式的应用程序的一部分运行。
关于&#34;如何保存数据&#34; 部分:
如果您不想使用任何高级ORM库(如Hibernate或任何其他JPA实现),您仍然可以使用普通的旧JDBC。或者至少有一些Spring-JDBC,它非常轻巧且易于使用。
&#34;你节省了什么&#34;
H2是一个关系数据库。所以无论你保存什么,它都会以列的形式出现。但!如果您确实不打算查询数据(既不在其上应用迁移脚本),则保存已经XML序列化的对象 是一个选项。您可以轻松定义一个ID + a&#34;数据&#34; varchar列,并保存您的xml。 H2DB中的数据长度没有限制。
注意:在关系数据库中保存XML通常不是一个好主意。我只建议您评估此选项,因为您似乎确信您只需要SQL实现可以提供的一组特定功能。
答案 1 :(得分:3)
不一致性和并发性有两种处理方式:
在应用程序级别无法很好地处理损坏的写入。文件系统应支持日志,它试图在某种程度上修复它。您也可以通过
执行此操作即使在最简单的关系数据库中也可以使用所有这些功能,例如: H2,SQLite甚至网页都可以在HTML5中使用这些功能。从头开始重新实现这些实在是太过分了,数据存储层的正确实现实际上会使您的简单需求变得非常复杂。
但是,仅仅是为了记录:
其他应用程序实例可能仍会尝试读取该文件,而其中一个应用程序正在编写该文件。这可能导致不一致(又称脏读)。确保在写入期间,编写器进程对文件具有独占锁定。如果无法获得独占访问锁定,则必须等待一段时间再重试。
读取文件的应用程序应读取它(如果它可以获得访问权限,其他实例不执行独占锁定),然后关闭该文件。如果无法读取(因为其他应用程序锁定),请等待并重试。
仍然是外部应用程序(例如记事本)可以更改xml。阅读文件时,您可能更喜欢独占读锁。
这里的想法是,如果您可能需要执行大量写操作(或者如果您以后可能想要回滚写入),则不希望触及真实文件。代替:
写入更改为单独的日记文件,由您的应用实例创建并锁定
您的应用实例未锁定主文件,它仅锁定日记文件
一旦完成所有写入操作,您的应用程序将打开具有独占写入锁定的真实文件,并提交日记文件中的每个更改,然后关闭该文件。
正如您所看到的,带锁的解决方案使文件成为共享资源,受锁的保护,一次只有一个应用程序可以访问该文件。这解决了并发问题,但也使文件访问成为瓶颈。因此,现代数据库(如Oracle)使用版本控制而不是锁定。版本控制意味着文件的旧版本和新版本同时可用。读者将由旧的,最完整的文件提供服务。完成新版本的编写后,它将合并到旧版本,新数据将立即可用。这实现起来比较棘手,但由于它允许并行读取所有应用程序,因此它可以更好地扩展。
答案 2 :(得分:2)
请注意,您的简单答案不会处理不同实例的并发写入。如果两个实例进行更改并保存,只需选择最新的实例将最终丢失其他实例的更改。正如其他答案所提到的,你应该尝试使用文件锁定。
一个相对简单的解决方案:
答案 3 :(得分:2)
回答你提到的三个问题:
为什么会导致不一致?如果你的意思是多个并发编辑会导致不一致,你只需要在编辑之前锁定文件。在文件旁边创建锁文件的最简单方法。在开始编辑之前,只需检查是否存在锁定文件。
如果要使其更具容错能力,还可以对文件设置超时。例如锁定文件有效期为10分钟。你可以在lockfile中编写一个随机生成的uuid,在保存之前,你可以检查uuid stil是否匹配。
我认为这与1号相同。
这可以通过使write atomic或文件不可变来解决。要使其成为原子,而不是直接编辑文件,只需复制文件,然后在副本上进行编辑。保存副本后,只需重命名文件。但是如果你想要更安全的一面,你总是可以做一些事情,比如在文件上附加时间戳,永远不要编辑或删除文件。因此,每次进行编辑时,都会创建一个副本,并在文件上附加一个较新的时间戳。阅读时,你会读到最新的一本。
答案 4 :(得分:0)
在考虑了一段时间之后,我想尝试像这样实现它:
data.<timestamp>.xml
- 文件。data.<timestamp>.xml
- 请勿覆盖并检查是否存在没有更新时间戳的文件。