1 DOM4j的使用
一种第三方专门用来使用Java代码来读取XML文件和创建XML文件的小技术,用于较为简单易用,被许多框架使用,取代了Java自己的解析XML的技术。
在Java中使用DOM4j前,需要将dom4j.jar的包导入工程中。
-
什么是jar包?
jar包(Java ARchive[档案、把…归档],Java归档)用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件,供使用者调用。相当于硬件连接电脑后需要安装的驱动。JVM虚拟机要使用、外挂一些其他厂商软件的程序或已写好的程序时,需要jar包作为驱动。 -
什么是API?
API(Application Programe Interface,应用程序接口)是制造或者发布厂商官方提供的一组接口(规范),一套工具,而不仅仅是一个文档,供用户通过API来实现自己的需求。
1.1 读XML
现有一个XML文档,需要使用DOM4j由Java程序打印到控制台。使用DOM4j来读取一个现成的XML文件,将内部的数据全部取出。
首先获取解析器SAXReader,再使用定义文档来绑定解析器,由文档再调用根元素,由根元素再向下依次调用各级子元素。获取元素后需要使用集合存放。如需获取集合中的属性,需要使用元素来调用,同样适用集合存放。
- 需要读取的XML文件:exec.xml
<?xml version="1.0" encoding="UTF-8"?>
<myroot>
<country cid="001" name="CHINA PR">
<province name="北京">
<area>朝阳区</area>
</province>
<province name="上海">
<area>黄浦区</area>
</province>
<province name="山东">
<city name="济南">
<area>历下区</area>
</city>
</province>
<province name="湖南">
<city name="长沙">
<area>岳麓区</area>
</city>
</province>
</country>
</myroot>
- 编写Java程序
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class ReadTest {
public static void main(String[] args) {
try {
// S1.引入dom4j提供的解析器,这个解析器就类似一台扫描仪
// Simple API XML,一个简单的解析XML的API
SAXReader saxReader = new SAXReader();
// S2.获取被扫描的XML文档,DOM模型已经成型
Document document = saxReader.read(new File("exec.xml"));
// S3.获取根元素
Element root = document.getRootElement();
System.out.println("根元素的名字是:" + root.getName());
// S4.获取一级子元素
List<Element> firstEles = root.elements();
for(Element firstEle : firstEles){
System.out.println("一级子元素的名字是:" + firstEle.getName());
// 获取一级子元素中的属性
List<Attribute> firstAttrs = firstEle.attributes();
for(Attribute attr : firstAttrs){
System.out.println("一级子元素中的属性名是:" + attr.getName());
System.out.println("一级子元素中的属性值是:" + attr.getValue());
}
// S5.获取二级子元素
List<Element> secondEles = firstEle.elements();
for(Element secondEle : secondEles){
System.out.println("二级子元素的名字是:" + secondEle.getName());
List<Attribute> secondAttrs = secondEle.attributes();
for(Attribute attr : secondAttrs){
System.out.println("二级子元素中的属性名是:" + attr.getName());
System.out.println("二级子元素中的属性值是:" + attr.getValue());
}
// S6.获取三级子元素
List<Element> thirdEles = secondEle.elements();
for(Element thirdEle : thirdEles){
// S7—1.如果三级元素中不再有嵌套元素,按照给出的XML,
// 可以直接获取标签内容
if(!"".equals(thirdEle.getText().trim())) System.out.println("三级子元素中嵌套的值是:" + thirdEle.getText());
// S7-2.如果三级元素中有嵌套元素,获取四级子元素
else {
List<Attribute> thirdAttrs = thirdEle.attributes();
for(Attribute attr : thirdAttrs){
System.out.println("三级子元素中的属性名是:" + attr.getName());
System.out.println("三级子元素中的属性值是:" + attr.getValue());
List<Element> forthList = thirdEle.elements();
for(Element forthEle : forthList){
System.out.println("四级子元素中嵌套的值是:" + forthEle.getText());
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2 写XML
现有一个写入了DOM模型对象的Java程序,需要使用DOM4j将Java程序写入XML文档。
package com.test;
import com.test.entity.WriteEntity;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class WriteTest {
public static void main(String[] args) {
try {
// S1.创建文档对象模型,此时模型为空
Document document = DocumentHelper.createDocument();
// S2.组装根元素
/**
* <myroot>
*
* </myroot>
* */
Element rootEle = document.addElement("myroot");
// S3.组装一级子元素
/**
* <myroot>
* <country>
*
* </country>
* </myroot>
* */
Element firstEle = rootEle.addElement("contry");
// 组装一级子元素的属性
/**
* <myroot>
* <country cid="001" name="CHINA PR">
*
* </country>
* </myroot>
* */
firstEle.addAttribute("cid", "001");
firstEle.addAttribute("name", "CHINA PR");
// 提取目标XML文档中变化的部分,搞一个实体类,减少代码量
WriteEntity we1 = new WriteEntity("北京", "", "朝阳区");
WriteEntity we2 = new WriteEntity("上海", "", "黄浦区");
WriteEntity we3 = new WriteEntity("山东", "济南", "历下区");
WriteEntity we4 = new WriteEntity("湖南", "长沙", "岳麓区");
List<WriteEntity> weList = new ArrayList<>();
Collections.addAll(weList, we1, we2, we3, we4);
// S4.将重复的部分循环添加
for(WriteEntity we : weList){
// 组装二级子元素
/**
* <myroot>
* <country cid="001" name="CHINA PR">
* <province name="">
*
* </province>
* </country>
* </myroot>
* */
Element secondEle = firstEle.addElement("province");
secondEle.addAttribute("name", we.getProvinceName());
// 若三级子元素中有城市,再添加四级子元素
if(!"".equals(we.getCityName())){
/**
* <myroot>
* <country cid="001" name="CHINA PR">
* <province name="">
* <city name="">
* <area>***</area>
* </city>
* </province>
* </country>
* </myroot>
* */
Element thirdEle = secondEle.addElement("city");
thirdEle.addAttribute("name", we.getCityName());
Element forthEle = thirdEle.addElement("area");
forthEle.setText(we.getArea());
}
// 若没有城市,直接是区,只添加到三级子元素
else{
/**
* <myroot>
* <country cid="001" name="CHINA PR">
* <province name="">
* <area name="">***</area>
* </city>
* </province>
* </country>
* </myroot>
* */
Element thirdEle = secondEle.addElement("area");
thirdEle.setText(we.getArea());
}
}
// S5.拿取字节输出流,表示输出到哪个路径下叫什么
OutputStream os = new FileOutputStream("exec_out.xml");
// S6.设置带换行的输出格式并指定编码(pretty,优雅的,也就是说比不优雅的多了个换行,让XML文档更好看)
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("utf-8");
// S7.输出到哪里/格式是什么,将前面定义好的引入,DOM4j自定义的字符输出流
XMLWriter xw = new XMLWriter(os, format);
// S8.将组装好的模型输出
xw.write(document);
// S9.关闭流
xw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
实体类
WriteEntity.java
package com.test.entity;
public class WriteEntity {
private String provinceName;
private String cityName;
private String area;
public WriteEntity() {}
public WriteEntity(String provinceName, String cityName, String area) {
this.provinceName = provinceName;
this.cityName = cityName;
this.area = area;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
}
效果图:
2 JDBC的使用
JDBC(Java DataBase Connectivity,Java数据库连接),由C语言的ODBC发展而来,是一种使用面向对象的Java程序操作关系型数据库的语言 ,也是最早的ORM(Object Relation Mapping,对象关系映射)工具。
JDBC库提供了一个底层API,用来支持独立于任何特定SQL实现的基本SQL功能。提供数据库访问的基本功能。 它是将各种数据库访问的公共概念抽取出来组成的类和接口。JDBC API包括两个包:java.sql(称之为JDBC内核API)和javax.sql(称之为JDBC标准扩展)。它们合在一起,包含了用Java开发数据库应用程序所需的类。
JDBC使得从Java程序发送SQL语句到数据库变得比较容易,并且适合所有SQL方言。只需要获知要连接数据库的驱动包名、链接写法就可以使用Java来操作数据库。 也就是说为一种数据库如Oracle写好了Java应用程序后,没有必要再为MS SQL Server再重新写一遍。而是可以针对各种数据库系统都使用同一个Java应用程序。使用JDBC API访问数据库时,我们要针对不同的数据库采用不同的驱动程序,驱动程序实际上是适合特定的数据库JDBC接口的具体实现, 它们一般具有建立一个与数据源(数据库)的连接、发送SQL语句到数据源、取回结果集三种功能。
- 常用数据库驱动和链接
Oracle
驱动:oracle.jdbc.driver.OracleDriver
URL:jdbc:oracle:thin:@<machine_name><:port>:dbname
注:machine_name:数据库所在的机器的名称;
port:端口号,默认是1521
thin:Oracle的子协议,也可以是oci
dbname:数据源(库)名
例:jdbc:oracle:thin:@localhost:1521:orcl
MySQL
驱动:com.mysql.jdbc.Driver
URL:jdbc:mysql://<machine_name><:port>/dbname
例:jdbc:mysql://localhost:3306/db_test
jdbc:mysql:///db_test(三斜线也表示本地)
注:machine_name:数据库所在的机器的名称;
port:端口号,默认3306
SQL server
驱动:com.microsoft.jdbc.sqlserver.SQLServerDriver
URL:jdbc:microsoft:sqlserver://<machine_name><:port>;DatabaseName=
注:machine_name:数据库所在的机器的名称;
port:端口号,默认是1433
- 与Mybatis、Hibernate之间的关系:
全手动:JDBC
半自动:Mybatis
全自动:Hibernate
2.1 使用ORM工具时JDBC的操作步骤
2.1.1 S1.创建关系表(关系型数据库中的表都称为关系表)
create table student(
id int primary key auto_increment,
name varchar(10) not null,
salary int not null,
birthday date not null
)
2.1.2 S2.创建实体类
一般将实体类放置在***.***.po或***.***.bean或***.***.entity包中。
注意:
- 与关系表重名(实体类首字母大写)
- 变量对应字段名
- 必须书写空参构造方法
- 酌情书写带参构造方法
- 酌情覆盖toString()(如果需要打印属性)
package com.test.po;
import java.util.Date;
public class Student {
private Integer id;
private String name;
private Integer salary;
private Date birthday;
public Student() {
}
public Student(Integer id, String name, Integer salary, Date birthday) {
this.id = id;
this.name = name;
this.salary = salary;
this.birthday = birthday;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", birthday=" + birthday +
'}';
}
}
2.1.3 S3.创建工厂类
工厂类是设计模式------工厂模式的一种表现方式,通过创建工厂类可获取一个将Java代码与数据库连接起来的连接,而在没有引入数据库连接池之前,一个工厂类只有一个连接供使用,所以在连接使用完毕后一定要关闭。
一般工厂类放置在***.***.factory包中。
package com.test.factory;
import java.sql.Connection;
import java.sql.DriverManager;
public class Factory {
//S1.加载不同关系型数据库厂商提供的驱动
//使用反射来进行驱动(类)的加载
//安装了这些驱动之后
//可以使用几乎相同的Java代码来操作不同语法的关系型数据库
//驱动程序仅仅安装一次,通常放置在静态初始化块中
static{
try {
//通过工厂获取连接
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
//S2.通过此方法来获取连接,
//方法设置为静态的原因是,
//方法不需要创建对象就可以被调用,
//另外方法在类创建时就可以被加载进来,不需在调用时再次创建,提高效率
public static Connection getConnection(){
try {
//连接数据源(database)
return DriverManager.getConnection("jdbc:mysql:///db_test",
"数据库用户名(MySQL一般是root)", "数据库密码");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//S3:此方法用来释放资源
//一般释放标准顺序为关闭结果集-->关闭执行器-->关闭连接
public static void close(ResultSet rs
, Statement st
, Connection con){
try{
if(rs!=null)
rs.close();
}catch(Exception ex){
ex.printStackTrace();
}finally{
try{
if(st!=null)
st.close();
}catch(Exception ex){
ex.printStackTrace();
}finally{
try{
if(con!=null)
con.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
}
2.1.4 S4.测试是否连通
package com.test;
import com.test.factory.Factory;
import java.sql.Connection;
public class TestConnection {
public static void main(String[] args) {
try {
Connection connection = Factory.getConnection();
// 常用的代替if-else的方法
// 在if中添加返回语句,else语句放在if的外面
if(connection == null){
System.out.println("Connection fail.");
return;
}
System.out.println("Connection success.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 测试连通后可以执行的操作
2.2.1 获取执行器(Statement)、使用结果集(ResultSet)接收查询数据
获取连接之后可以拿取执行器,执行器的作用是用来执行SQL语句,常用的两种执行器Statement、PreparedStatement。 两种执行器都可以根据SQL语句的不同类别调用不同的方法。这里先以Statement执行器来举例。
2.2.1.1 boolean execute()
执行DQL语句返回true,执行DML语句返回false,但DML语句依然可以运行。因逻辑有些奇怪,使用的场合不多。
package com.test;
import com.test.factory.Factory;
import java.sql.Connection;
import java.sql.Statement;
public class TestConnection {
public static void main(String[] args) {
try {
Connection connection = Factory.getConnection();
// 获取执行器
Statement statement = connection.createStatement();
// 定义要被执行的DML、DQL语句,
// 如果语句写错,会报sql syntax error错误
String dql1 = "select * from student";
String dml1 = "insert into student values(null, 'zs', 1000, '1991-05-05')";
String dml2 = "update student set name='zs111' where name='zs'";
String dml3 = "delete from student where name='zs111'";
boolean exec_dql = statement.execute(dql1);
System.out.println("exec_dql------->" + exec_dql);
boolean exec_dml1 = statement.execute(dml1);
System.out.println("exec_dml1------->" + exec_dml1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
效果图:
成功插入一条数据
2.2.1.2 int executeUpdate()
用来执行DML语句,返回值是一个整数,表示受影响的行数。执行DQL语句立刻报错。(延续上面的代码继续执行)
在executeUpdate()方法中执行DQL语法的报错:
String dql1 = "select * from student";
String dml1 = "insert into student values(null, 'zs', 1000, '1991-05-05')";
//更新所有符合要求的数据
String dml2 = "update student set name='zs111' where name='zs'";
//删除所有符合要求的数据
String dml3 = "delete from student where name='zs111'";
System.out.println("已添加了" + statement.executeUpdate(dml1) + "条新数据");
System.out.println("已修改了" + statement.executeUpdate(dml2) + "条数据");
System.out.println("已删除了" + statement.executeUpdate(dml3) + "条数据");
效果图:
数据库已不存在数据
2.2.1.3 ResultSet executeQuery()
执行DQL语句,返回一个结果集。执行DML语句立刻报错。返回的结果集类型是java.sql.ResultSet。返回的数据全部封装在结果集中,通过解析可以拿取返回的数据。
在进行数据的拿取之前首先要判断是否存在有效数据,不能根据结果集是否为null,来判断是否存在有效数据,因为结果集永远不可能为null,至少有一行表头。
(student表添加了一个passwd字段)
通过.next()是否返回true来判断指针能否下移,如果可以下移,则返回true表示有有效数据;当返回false时,则指针无法移动了,没有有效数据。可以通过使用所指行的get数据类型(列数)来返回数据,还可以使用get数据类型(列名)返回数据。
String dql1 = "select * from student";
ResultSet rs = statement.executeQuery(dql1);
// 可以通过使用所指行的get数据类型(列数)来返回数据,
// 也可以使用get数据类型(列名)返回数据。
// 结果集中的事件类型,因为传入时已格式化,所以打印时直接输出即可。
while(rs.next()){
System.out.println("学号:" + rs.getInt(1)
+ "\t姓名:" + rs.getString("name")
+ "\t密码:" + rs.getString(3)
+ "\t薪资:" + rs.getInt("salary")
+ "\t生日:" + rs.getDate(5));
}
2.2.1.4 int[] executeBatch()
批处理SQL语句的方法,注意不能处理DQL语句,且需要关闭MySQL自动提交事务的功能。MySQL事务默认自动提交,但是效率太低。
批处理且手动提交像一把自动步枪,把弹夹中压入子弹后可以连发,而不需要打一发上一次子弹,前者明显效率更高。
大批量处理数据时,使用批处理且关闭自动事务提交,使用手动提交事务,效率最高。像下面的例子:
package com.test;
import com.test.factory.Factory;
import java.sql.Connection;
import java.sql.Statement;
public class TestBatch {
public static void main(String[] args) throws Exception{
String sql1 = "insert into student values(null, 'Eric1', '12345', 3000, '1990-06-03')";
String sql2 = "insert into student values(null, 'Ruby', '123456', 4000, '1993-06-08')";
String sql3 = "insert into student values(null, 'Cindy', '1234567', 2500, '1990-07-09')";
String sql4 = "delete from student where id > 5";
// 获取连接
Connection connection = Factory.getConnection();
// 关闭mysql设置的自动提交事务功能,MySQL写完执行就自动提交了,这一点跟Oracle不同
// 批处理中若出错中断,自动提交出错后会造成前后修改/未修改状态不一致,所以需要先将自动提交取消
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
statement.addBatch(sql1);
statement.addBatch(sql2);
statement.addBatch(sql3);
statement.addBatch(sql4);
int[] count = statement.executeBatch();
// 需要自行提交事务
connection.commit();
for(int a : count){
System.out.println("更改的记录数是" + a);
}
}