本文主要介绍了后端开发中比较小众的一种数据库技术:BaseX。本文讲解Java结合Xquery集成BaseX的案例。
1、集成准备工作
首先是环境的搭建,技术的选型。开发工具的使用。Oxygen是一个数据库可视化连接工具,也是比较小众。
开发工具:
IDEA
Oxygen
技术:
Java
BaseX
Xpath
Xquery
BaseX需要阅读的文档:
- https://github.com/BaseXdb/basex/blob/9/basex-examples/src/main/java/org/basex/examples/api/Example.java
- https://docs.basex.org/wiki/Table_of_Contents
2、集成案例目录结构
我们首先新建一个如下的项目目录结构。这里要注意的是,我们的 Xquery文件是放在 resource文件下的。我们要完成的是 BaseXClient和Example文件中的代码。
总结:Base X
相当于一个工具类,Example
是我们写的创建XML数据库的例子。
3、IDEA配置集成BaseX
按照下图的方式进行IDEA的配置:
总结:BaseX配置的端口一定是:1984 这里不能乱填!
4、集成工具类BaseXClient的创建
创建一个BaseXClient.java文件,写入下面的代码:
package com.linghu.util; import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** * Java client for BaseX. * Works with BaseX 7.0 and later * * Documentation: https://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-22, BSD License */ public final class BaseXClient implements Closeable { /** UTF-8 charset. */ private static final Charset UTF8 = Charset.forName("UTF-8"); /** Output stream. */ private final OutputStream out; /** Input stream (buffered). */ private final BufferedInputStream in; /** Socket. */ private final Socket socket; /** Command info. */ private String info; /** * Constructor. * @param host server name * @param port server port * @param username user name * @param password password * @throws */ public BaseXClient(final String host, final int port, final String username, final String password) throws IOException { socket = new Socket(); socket.setTcpNoDelay(true); socket.connect(new InetSocketAddress(host, port), 5000); in = new BufferedInputStream(socket.getInputStream()); out = socket.getOutputStream(); // receive server response final String[] response = receive().split(":"); final String code, nonce; if(response.length > 1) { // support for digest authentication code = username + ':' + response[0] + ':' + password; nonce = response[1]; } else { // support for cram-md5 (Version < 8.0) code = password; nonce = response[0]; } send(username); send(md5(md5(code) + nonce)); // receive success flag if(!ok()) throw new IOException("Access denied."); } /** * Executes a command and serializes the result to an output stream. * @param command command * @param output output stream * @throws IOException Exception */ public void execute(final String command, final OutputStream output) throws IOException { // send {Command}0 send(command); receive(in, output); info = receive(); if(!ok()) throw new IOException(info); } /** * Executes a command and returns the result. * @param command command * @return result * @throws IOException Exception */ public String execute(final String command) throws IOException { final ByteArrayOutputStream os = new ByteArrayOutputStream(); execute(command, os); return new String(os.toByteArray(), UTF8); } /** * Creates a query object. * @param query query string * @return query * @throws IOException Exception */ public Query query(final String query) throws IOException { return new Query(query); } /** * Creates a database. * @param name name of database * @param input xml input * @throws IOException I/O exception */ public void create(final String name, final InputStream input) throws IOException { send(8, name, input); } /** * Adds a document to a database. * @param path path to resource * @param input xml input * @throws IOException I/O exception */ public void add(final String path, final InputStream input) throws IOException { send(9, path, input); } /** * Replaces a document in a database. * @param path path to resource * @param input xml input * @throws IOException I/O exception */ public void replace(final String path, final InputStream input) throws IOException { send(12, path, input); } /** * Stores a binary resource in a database. * @param path path to resource * @param input xml input * @throws IOException I/O exception */ public void store(final String path, final InputStream input) throws IOException { send(13, path, input); } /** * Returns command information. * @return string info */ public String info() { return info; } /** * Closes the session. * @throws IOException Exception */ @Override public void close() throws IOException, IOException { send("exit"); out.flush(); socket.close(); } /** * Checks the next success flag. * @return value of check * @throws IOException Exception */ private boolean ok() throws IOException { out.flush(); return in.read() == 0; } /** * Returns the next received string. * @return String result or info * @throws IOException I/O exception */ private String receive() throws IOException { final ByteArrayOutputStream os = new ByteArrayOutputStream(); receive(in, os); return new String(os.toByteArray(), UTF8); } /** * Sends a string to the server. * @param string string to be sent * @throws IOException I/O exception */ private void send(final String string) throws IOException { out.write((string + '\0').getBytes(UTF8)); } /** * Receives a string and writes it to the specified output stream. * @param input input stream * @param output output stream * @throws IOException I/O exception */ private static void receive(final InputStream input, final OutputStream output) throws IOException { for(int b; (b = input.read()) > 0;) { // read next byte if 0xFF is received output.write(b == 0xFF ? input.read() : b); } } /** * Sends a command, argument, and input. * @param code command code * @param path name, or path to resource * @param input xml input * @throws IOException I/O exception */ private void send(final int code, final String path, final InputStream input) throws IOException { out.write(code); send(path); send(input); } /** * Sends an input stream to the server. * @param input xml input * @throws IOException I/O exception */ private void send(final InputStream input) throws IOException { final BufferedInputStream bis = new BufferedInputStream(input); final BufferedOutputStream bos = new BufferedOutputStream(out); for(int b; (b = bis.read()) != -1;) { // 0x00 and 0xFF will be prefixed by 0xFF if(b == 0x00 || b == 0xFF) bos.write(0xFF); bos.write(b); } bos.write(0); bos.flush(); info = receive(); if(!ok()) throw new IOException(info); } /** * Returns an MD5 hash. * @param pw String * @return String */ private static String md5(final String pw) { final StringBuilder sb = new StringBuilder(); try { final MessageDigest md = MessageDigest.getInstance("MD5"); md.update(pw.getBytes()); for(final byte b : md.digest()) { final String s = Integer.toHexString(b & 0xFF); if(s.length() == 1) sb.append('0'); sb.append(s); } } catch(final NoSuchAlgorithmException ex) { // should not occur ex.printStackTrace(); } return sb.toString(); } /** * Inner class for iterative query execution. */ public class Query implements Closeable { /** Query id. */ private final String id; /** Cached results. */ private ArrayList<byte[]> cache; /** Cache pointer. */ private int pos; /** * Standard constructor. * @param query query string * @throws IOException I/O exception */ Query(final String query) throws IOException { id = exec(0, query); } /** * Binds a value to an external variable. * @param name name of variable * @param value value * @throws IOException I/O exception */ public void bind(final String name, final String value) throws IOException { bind(name, value, ""); } /** * Binds a value with the specified type to an external variable. * @param name name of variable * @param value value * @param type type (can be an empty string) * @throws IOException I/O exception */ public void bind(final String name, final String value, final String type) throws IOException { cache = null; exec(3, id + '\0' + name + '\0' + value + '\0' + type); } /** * Binds a value to the context item. * @param value value * @throws IOException I/O exception */ public void context(final String value) throws IOException { context(value, ""); } /** * Binds a value with the specified type to the context item. * @param value value * @param type type (can be an empty string) * @throws IOException I/O exception */ public void context(final String value, final String type) throws IOException { cache = null; exec(14, id + '\0' + value + '\0' + type); } /** * Checks for the next item. * @return result of check * @throws IOException I/O exception */ public boolean more() throws IOException { if(cache == null) { out.write(4); send(id); cache = new ArrayList<>(); final ByteArrayOutputStream os = new ByteArrayOutputStream(); while(in.read() > 0) { receive(in, os); cache.add(os.toByteArray()); os.reset(); } if(!ok()) throw new IOException(receive()); pos = 0; } if(pos < cache.size()) return true; cache = null; return false; } /** * Returns the next item. * @return item string * @throws IOException I/O Exception */ public String next() throws IOException { return more() ? new String(cache.set(pos++, null), UTF8) : null; } /** * Returns the whole result of the query. * @return query result * @throws IOException I/O Exception */ public String execute() throws IOException { return exec(5, id); } /** * Returns query info in a string. * @return query info * @throws IOException I/O exception */ public String info() throws IOException { return exec(6, id); } /** * Returns serialization parameters in a string. * @return query info * @throws IOException I/O exception */ public String options() throws IOException { return exec(7, id); } /** * Closes the query. * @throws IOException I/O exception */ @Override public void close() throws IOException { exec(2, id); } /** * Executes the specified command. * @param code command code * @param arg argument * @return resulting string * @throws IOException I/O exception */ private String exec(final int code, final String arg) throws IOException { out.write(code); send(arg); final String s = receive(); if(!ok()) throw new IOException(receive()); return s; } } }
5、Example创建并连接Basex数据库
接下来开始创建数据库:
package com.linghu.util; import java.io.IOException; import java.io.OutputStream; /** * This example shows how commands can be executed on a server. * * This example requires a running database server instance. * Documentation: https://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-22, BSD License */ public final class Example { /** * Main method. * @param args command-line arguments * @throws IOException I/O exception */ public static void main(final String... args) throws IOException { // create session try(BaseXClient session = new BaseXClient("localhost", 1984, "admin", "admin")) { session.query("db:create('Zhang')").execute(); } } }
总结:执行如上代码即可生成上图的数据库、
6、关于BaseX数据库
BaseX是一种用于处理和存储XML数据的数据库管理系统。它是一个开源的XML数据库,具有快速、高效和灵活的特点。
BaseX支持多种查询语言,包括XQuery和XPath。它允许用户通过这些语言对XML数据进行查询、更新和删除操作。BaseX还提供了强大的索引和优化功能,以提高查询和访问性能。
BaseX可以在多个平台上运行,包括Windows、Mac和Linux。它可以作为一个独立服务器运行,也可以嵌入到其他应用程序中使用。