Java数据库编程、XML解析技术
数据库编程
JDBC概述
是Java Database Connecive,即数据库连接技术的简称,它提供了连接各种常用数据库的能力。
是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC组成
通常JDBC有两部分组成:JDBC API和JDBC Driver。
1)主要使用JDBC API完成三件事情:与数据库建立连接、执行SQL语句、处理结果。
2)JDBC Driver常见的有以下几类:
JDBC-ODBC桥加ODB(驱动程序):缺点却是执行效率低、功能不够强大
JDBC+本地驱动:安全性比较差。
JDBC网络纯Java驱动程序:将JDBC转换为与DBMS无关的网络协议,又被某服务器转换为一种DBMS协议,以操作各种数据库
本地协议纯Java驱动程序:缺点是访问不同的数据库需要下载专用的JDBC驱动。
JDBC应用步骤
- 加载JDBC Driver
- 创建数据库连接(Connection)
- 创建一个Statement
- 执行sql语句(CRUD操作语句)
- 处理sql结果
- 关闭Statement
- 关闭连接Connection
1)加载JDBC Driver
1 try { 2 //加载MySQL JDBC驱动 3 Class.forName("com.mysql.jdbc.Driver"); 4 } catch (ClassNotFoundException e) { 5 e.printStackTrace(); 6 }
2)创建数据库连接
1 Connection connection = null; 2 String url = "jdbc:mysql://localhost:3306/jdbc"; // 连接字符串 3 String user = "root"; // 用户名 4 String pass = "123456"; // 密码 5 try { 6 // 创建Connection对象 7 connection = DriverManager.getConnection(url, user, pass); 8 } catch (SQLException e) { 9 e.printStackTrace(); 10 }
3)创建Statement对象,执行SQL语句
// 创建Statement对象 Statement stmt = connection.createStatement();
// 发送Insert语句 int count = stmt.executeUpdate("INSERT INTO users(username, password) VALUES ('lily', 'lily123')");
// 发送查询SQL语句 ResultSet rs = stmt.executeQuery("SELECT id, username, password FROM users");
4)处理结果
// 循环处理查询结果集 while (rs.next()){ System.out.println("编号:" + rs.getInt(1) + ", 用户名:" + rs.getString("username") + ", 密码:" + rs.getString("password")); }
5)释放资源
try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (connection != null) connection.close(); } catch (SQLException e) { e.printStackTrace(); }
关于PreparedStatement
从代码易读性上来说,因为PreparedStatement使用“?”来代替参数,所以SQL语句较使用Statement更容易阅读。
从安全性上来说,PreparedStatement是通过“?”来传递参数的,避免了拼sql而出现sql注入的问题,所以安全性较好。在开发中,推荐使用PreparedStatement。
String username = "admin"; String password = "admin"; // 创建Statement对象 Statement stmt = connection.createStatement(); // 发送SQL语句并执行 ResultSet rs = stmt.executeQuery("SELECT id, username, password FROM users WHERE username='" + username + "' AND password='" + password + "'"); // 创建PreparedStatement对象 PreparedStatement pstmt = connection.prepareStatement("SELECT id, username, password FROM users WHERE username=? AND password=?"); // 设置pstmt参数 pstmt.setString(1, username); pstmt.setString(2, password); // 执行SQL语句 ResultSet rs2 = pstmt.executeQuery();
连接到数据库
1)使用DriverManager类的静态方法getConnection();
2)使用DataSource类的getConnection();
/** * 加载配置文件中JDBC连接所需要数据 */ private static void loadProperties() { InputStream in = Thread.currentThread().getClass() .getResourceAsStream("/jdbc.properties"); Properties prop = new Properties(); try { prop.load(in); } catch (IOException e) { throw new RuntimeException(e); } driver = prop.getProperty("driver"); url = prop.getProperty("url"); user = prop.getProperty("user"); password = prop.getProperty("password"); }
/* 通过静态代码块加载数据库驱动 */ static { loadProperties(); // 读取配置文件 try { Class.forName(driver); // 加载驱动 } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }
/** * 获取连接对象 * * @return 连接对象 * @throws SQLException * 无法建立连接,则抛出异常 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); }
数据库会话
/** * 开启会话 */ public void open() { if (null == conn) try { conn = JdbcResourceManager.getConnection(); } catch (SQLException e) { throw new JdbcSessionException("数据库会话创建失败", e); } } /** * 关闭数据库会话,释放资源 */ public void close() { try { JdbcResourceManager.close(rs); rs = null; } catch (SQLException e) { throw new JdbcSessionException("数据库会话ResultSet关闭失败", e); } finally { try { JdbcResourceManager.close(pstmt); pstmt = null; } catch (SQLException e) { throw new JdbcSessionException("数据库会话Statement关闭失败", e); } finally { try { JdbcResourceManager.close(conn); conn = null; } catch (SQLException e) { throw new JdbcSessionException("数据库会话Connection关闭失败", e); } } } }
执行INSERT、UPDATE、DELETE语句的方法:
/** * 执行更新SQL语句 * * @param sql * 更新的SQL语句,如INSERT、UPDATE、DELETE语句 * @param params * 用于替换更新语句中"?"占位符的参数列表 * @return 受影响行数 */ public int executeUpdate(String sql, Object... params) { try { pstmt = conn.prepareStatement(sql); if (null != params) { for (int i = 0; i < params.length; i++) { // 为每个?占位符设置参数 pstmt.setObject(i + 1, params[i]); } } return pstmt.executeUpdate(); } catch (SQLException e) { throw new JdbcSessionException("执行更新操作失败", e); } }
用于执行SELECT语句的方法:
/** * 执行查询SQL语句 * * @param sql * 待执行查询的SQL语句,如 SELECT 语句 * @param params * 用于替换查询语句中"?"占位符的参数列表 * @return 查询结果集ResultSet对象 */ public ResultSet executeQuery(String sql, Object... params) { try { pstmt = conn.prepareStatement(sql); if (null != params) { for (int i = 0; i < params.length; i++) { // 为每个?占位符设置参数 pstmt.setObject(i + 1, params[i]); } } return rs = pstmt.executeQuery(); } catch (SQLException e) { throw new JdbcSessionException("执行查询操作失败", e); } }
JDBC之DAO模式
主要功能:是实现对持久化数据的访问。
DAO的组成部分包括以下四个内容:
- 数据库操作辅助类:实现数据库操作辅助功能,如获取连接,关闭连接等
- 实体类:用于数据传递
- DAO接口:约定DAO功能
- DAO接口的实现类:具体实现DAO功能
使用JDBC处理MySQL大文本和大数据
向MySQL中存储大文本,可调用JDBC API中PreparedStatement的如下方法:
// 将指定参数设置为给定 Reader 对象 void setCharacterStream(int parameterIndex, Reader reader) throws SQLException // 将给定参数设置为给定 Reader 对象,该对象具有给定字符数长度(int型) void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException // 将指定参数设置为给定 Reader 对象,该对象具有给定字符数长度(long型) void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException
从MySQL数据库中获取大文本列字段值,则可以使用ResultSet的如下方法:
// 以 java.io.Reader 对象的形式获取此 ResultSet 对象的当前行中指定列的值 Reader getCharacterStream(int columnIndex) throws SQLException Reader getCharacterStream(String columnLabel) throws SQLException // 以 String 的形式获取此 ResultSet 对象的当前行中指定列的值 String getString(int columnIndex) throws SQLException String getString(String columnLabel) throws SQLException
向MySQL中存储二进制数据,可调用JDBC API中PreparedStatement的如下方法:
// 将指定参数设置为给定输入流。 void setBinaryStream(int parameterIndex, InputStream x) // 将指定参数设置为给定输入流,该输入流将具有给定字节数(int型)。 void setBinaryStream(int parameterIndex, InputStream x, int length) // 将指定参数设置为给定输入流,该输入流将具有指定字节数(long型)。 void setBinaryStream(int parameterIndex, InputStream x, long length)
需要从MySQL数据库中获取二进制列字段值,则可以使用ResultSet的如下方法:
// 以未解释字节的流的形式获取此 ResultSet 对象的当前行中指定列的值。 InputStream getBinaryStream(int columnIndex) // 以未解释的 byte 流的形式获取此 ResultSet 对象的当前行中指定列的值。 InputStream getBinaryStream(String columnLabel) // 以 Java 编程语言中 Blob 对象的形式获取此 ResultSet 对象的当前行中指定列的值。 Blob getBlob(int columnIndex) // 以 Java 编程语言中 Blob 对象的形式获取此 ResultSet 对象的当前行中指定列的值。 Blob getBlob(String columnLabel)
XML解析技术
XML概述:
被设计用来传输和存储数据的可扩展标记语言。
通过XML我们可以自定义自己的标签:
<mail>
<from>tom</from>
<to>lily</to>
<detail>I will go home this weekend!</detail>
</mail>
XML是一种树结构。树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构
XML文档必须包含根元素,该元素是所有其他元素的父元素
XML语法规则
XML声明,如:
<?xml version="1.0" encoding="utf-8"?>
它定义XML的版本(1.0)和所使用的编码(utf-8)字符集。
所有的XML元素都是成对出现的,有一个开始标签,就必须有一个关闭标签。
XML标签对大小写敏感,标签<Letter>与标签<letter>是不同的。
XML文档必须有根元素,标签如果需要嵌套,必须正确嵌套,在内层打开的标签就必须在内层关闭。
XML标签如果需要添加属性,属性值必须加引号。
在XML中编写注释的语法与HTML的语法很相似,都是 <!– This is a comment –>。
在XML中,空格会被保留,不像HTML中,加再多的空格最终都只合并为一个。
XML命名规则
XML元素必须遵循以下命名规则:
- 名称可以包含字母、数字以及其他的字符
- 名称不能以数字或者标点符号开始
- 名称不能以字母 xml(或者 XML、Xml 等等)开始
- 名称不能包含空格
- 可使用任何名称,没有保留的字词。
DTD
作用:是定义XML文档的合法构建模块
声明一个元素:
<!ELEMENT 元素名称 (元素内容)>
空元素:
<!ELEMENT 元素名称 EMPTY>
PCDATA 的元素通过圆括号中的#PCDATA进行声明:
<!ELEMENT 元素名称 (#PCDATA)>
带任何元素的内容:
<!ELEMENT 元素名称 ANY>
带有子元素(序列)的元素:
<!ELEMENT 元素名称 (子元素名称 1,子元素名称 2,.....)>
注意:
当子元素按照由逗号分隔开的序列进行声明时,这些子元素必须按照相同的顺序出现在文档中。在一个完整的声明中,子元素也必须被声明,同时子元素也可拥有子元素。“mail”元素的完整声明是:
<!ELEMENT mail (from,to,detail)>
<!ELEMENT mail (from,to,detail)> <!ELEMENT from (#PCDATA)> <!ELEMENT to (#PCDATA)> <!ELEMENT detail (#PCDATA)>声明只出现一次的元素:
<!ELEMENT 元素名称 (子元素名称)>
声明最少出现一次的元素:
<!ELEMENT 元素名称 (子元素名称+)>
声明出现0次或多次的元素:
<!ELEMENT 元素名称 (子元素名称*)>
声明出现0次或1次的元素:
<!ELEMENT 元素名称 (子元素名称?)>
声明属性
<!ATTLIST 元素名称 属性名称 属性类型 默认值>
CDATA - (未解析)字符数据
CDATA部分不能包含字符串“]]>”,也不允许嵌套的CDATA部分。
标记CDATA部分结尾的“]]>”不能包含空格或换行。
<script> <![CDATA[ function matchwo(a,b) { if (a < b && a < 0) return 1; else return 0; } ]]> </script>
内部的DOCTYPE声明
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mail[ <!ELEMENT mail (from,to,detail)> <!ELEMENT from (#PCDATA)> <!ELEMENT to (#PCDATA)> <!ELEMENT detail (#PCDATA)> ]> <mail> <from>tom</from> <to>lily</to> <detail>I will go home this weekend!</detail> </mail>
外部文档声明
<!ELEMENT mail (from,to,detail)> <!ELEMENT from (#PCDATA)> <!ELEMENT to (#PCDATA)> <!ELEMENT detail (#PCDATA)>
XML文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mail SYSTEM "mail.dtd">
<mail>
<from>tom</from>
<to>lily</to>
<detail>I will go home this weekend!</detail>
</mail>
使用dom4j操作xml
-
创建Document对象
Document document = DocumentHelper.createDocument();
Element root = document.addElement(“students”); // 根元素节点
-
读取XML文件时,获取Document对象
SAXReader reader = new SAXReader();
Document document = reader.read(new File(“student.xml”));
-
字符串解析为XML时,获取Document对象
String text = “tom“;
Document document = DocumentHelper.parseText(text);
/** * 创建XML文件 * * @throws IOException */ public void createXml() throws IOException { // 创建Document对象 Document document = DocumentHelper.createDocument(); // 根元素节点 Element root = document.addElement("students"); root.addComment("all students info"); // 添加注释 Element student = root.addElement("student"); // 根元素节点添加子节点 student.addAttribute("id", "001"); // 添加节点属性 Element name = student.addElement("name"); name.addText("tommy"); // 设置节点文本 Element age = student.addElement("age"); age.addText("18"); // 设置XML美化格式 OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding("utf-8"); // 编码字符集设置 // xml写入器 XMLWriter writer = new XMLWriter(new FileWriter("students.xml"), format); // 写操作 writer.write(document); writer.flush(); writer.close(); }
/** * 修改xml文件 * * @throws Exception */ public void modifyXml() throws Exception { // 创建SAXReader对象 SAXReader reader = new SAXReader(); Document document = reader.read(new File("students.xml")); /** 修改内容之一: 如果student节点中id属性的内容为001,则修改成1001。因为查找节点使用到了Xpath,所以还需要将 jaxen-1.1-beta-6.jar 包添加到项目中 */ List list = document.selectNodes("/students/student/@id"); Iterator it = list.iterator(); while (it.hasNext()) { Attribute attr = (Attribute) it.next(); if ("001".equals(attr.getValue())) attr.setValue("1001"); } /** * 修改内容之二: name项内容若为"tommy",则改为"tom" */ list = document.selectNodes("/students/student/name"); it = list.iterator(); while (it.hasNext()) { Element name = (Element) it.next(); if ("tommy".equals(name.getText())) { name.setText("tom"); } } /** 修改内容之三: 将id="1001"的学生age节点删除,再添加birth节点 */ list = document.selectNodes("/students/student/@id"); it = list.iterator(); while (it.hasNext()) { Attribute attr = (Attribute) it.next(); if ("1001".equals(attr.getValue())) { Element parent = attr.getParent(); Iterator iterator = parent.elementIterator("age"); if (iterator.hasNext()) { Element elem = (Element) iterator.next(); parent.remove(elem); } Element birth = parent.addElement("birth"); birth.setText("1995-01-01"); break; } } /* 将document中的内容写入文件中 */ // 设置XML美化格式 OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding("utf-8"); // 编码字符集设置 // xml写入器 XMLWriter writer = new XMLWriter(new FileWriter("students.xml"), format); writer.write(document); writer.close(); }
/** * 遍历节点 * * @throws Exception */ public void readXml() throws Exception { // 创建SAXReader对象 SAXReader reader = new SAXReader(); // 加载xml文件 Document document = reader.read(new File("students.xml")); // 获取根元素节点 Element root = document.getRootElement(); // 递归遍历所有节点内容 read(root); } /** * 读节点信息 * * @param element * student节点 */ private void read(Element element) { for (int i = 0, size = element.nodeCount(); i < size; i++) { Node node = element.node(i); // 获取元素的子节点 if (node instanceof Comment) // 注释节点 continue; if (node instanceof Element) { // 元素节点 Element elem = (Element) node; System.out.print(elem.getName() + " : "); if ("student".equals(node.getName())) { // 是student节点 System.out.println(); Iterator attr = elem.attributes().iterator(); // student节点属性 while (attr.hasNext()) { Attribute at = (Attribute) attr.next(); System.out .println(at.getName() + " : " + at.getValue()); } } read((Element) node); } else if (node instanceof Text) { // 文本节点 if (!"".equals(node.getText().trim())) System.out.println(node.getText()); } } }
XPath 简介
在XPath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。
节点关系:父、子、同胞、先辈、后代