Java与XML 连接池-JDBC 笔记

Posted 汇宁学习网

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java与XML 连接池-JDBC 笔记相关的知识,希望对你有一定的参考价值。

简介 ***

可扩展标记语言.

特性:
XML具有平台无关性 , 是一门独立的标记语言.
XML具有自我描述性, 主要应用于: 数据存储, 网络数据传输 , 配置文件的使用.

我们学习的重点:
XML语法的格式 , 以及XML文件如何编写 . 后续配置文件的使用

XML语法 *****

XML文档通常存储在.xml文件中.

1. XML文档声明
<?xml version="1.0" encoding="UTF-8"?>

2. 标记(标签/元素/节点)

在XML中国, 我们通过标记, 来描述文档.
语法:
开始标记 : <标记名称>
结束标记 : </标记名称>
标记名称 : 自定义的名称, 命名规则可参考Java中的标识符命名规则
标记内容 : 在开始标记 与 结束标记之间可以编写内容.

例如 , 通过标记描述一个人的姓名:
<name>张三</name>

3. 标记 , 可以嵌套! (不允许交叉)
例如: 通过标记描述一个人
<person>
<name>张三</name>
<age>18</age>
</person>

4. 一个XML文档中, 必须有且仅允许有一个根标记

例如: 描述两个人:
正确例子:
<persons>
<person>
<name>小泽马老师</name>
<age>18</age>
</person>
<person>
<name>武藤马老师</name>
<age>18</age>
</person>
</persons>
错误例子:
<person>
<name>小泽马老师</name>
<age>18</age>
</person>
<person>
<name>武藤马老师</name>
<age>18</age>
</person>

5. 标记名称, 允许重复.
6. 子标记 父标记 兄弟标记 后代标记 祖先标记
案例
<persons>
<person>
<name>小泽马老师</name>
<age>18</age>
</person>
<person>
<name>武藤马老师</name>
<age>18</age>
</person>
</persons>

案例中: person是persons的子标记
person是name的父标记
name是age的兄弟标记
name是person和persons的后代标记
persons是name和age的祖先标记

7. 标记的属性.
标记中可以包含属性, 属性用于描述标记的相关数据.
语法:
在开始标记中 , 描述标记的属性.
一个标记可以包含0-n个属性, 每一个属性由一个键值对组成.
键与值之间使用等号连接, 多个键值对之间 使用空格分割.
属性值 , 必须被引号引住 (单双引号都可以)

案例:
<persons>
<person id="10001" sex="未知">
<name>小泽马老师</name>
<age>18</age>
</person>
<person>
<name>武藤马老师</name>
<age>18</age>
</person>
</persons>

8. XML文件的注释
多行注释:
注释的开始: <!--
注释的结束: -->

案例:

描述一本书:
图书编号: 10001
图书名称: 金苹果
图书简介: 讲述了果农辛勤劳动 , 丰收的过程.

代码:

<?xml version="1.0" encoding="UTF-8"?>
<book id="10001">
<name>金苹果</name>
<info>讲述了果农辛勤劳动 , 丰收的过程.</info>
</book>

练习1

描述五本书:
每本书拥有: 图书编号 / 图书名称 / 图书简介

练习2

描述五个人:
每个人包含: 姓名 / 年龄 / 性别

Java中XML的解析 掌握

面试题: *****

题目: Java中有几种XML解析方式, 分别是什么, 有什么样的优缺点
答:
共四种解析XML的方式 ,

1. SAX解析
事件驱动的解析机制 ,
由解析器逐行读取XML文件 , 每当发现一个节点的开始 / 结束 / 文本内容 / 属性时. 触发事件

优点:
读取大文件时 , 不会导致内存溢出.
原因是: 逐行读取, 当读取下一行数据时, 上一行的数据已经从内存中释放了.

缺点:
1. 因为逐行读取+事件驱动, 导致解析的流程无法回头. 某一个节点解析时未处理 , 后续就没办法再处理了.
2. 在事件处理时, 需要程序员自己通过程序来标记节点层次
3. 因为逐行读取 , 效率较低.
4. sax解析是只读解析方式, 无法修改xml的源文件内容.

2. DOM解析
直接将整个文档, 加载到内存 . 在内存中建立文档树模型 , 然后通过操作文档树 , 来完成数据的解析与修改
优点:
1. 文档整体 被加载到内存, 在加载时虽然耗费了一些内存, 但是在解析时效率很高.
2. 文档被加载到内存中, 可以任意读取 / 修改 / 删除

缺点:
无法解析大文件 , 容易造成内存的溢出.

3. JDOM解析
是DOM解析的扩展, 优缺点一致

4. DOM4J解析
是DOM解析的扩展, 优缺点一致

DOM4J解析步骤 熟悉

步骤:
1. 引入dom4j.jar文件

2. 创建一个指向XML文档的输入流.
InputStream is = ...
3. 创建一个XML读取工具对象
SAXReader sr = new SAXReader();
4. 读取xml文档输入流 , 并得到文档对象.
Document doc = sr.read(is);
5. 通过文档对象, 获取文档中的根节点
Element root = doc.getRootElement();

Element 常用方法

-   获取节点名称 .
String getName();

- 获取文本内容
String getText();

- 设置文本内容
void setText(String text);

- 根据名称, 获取匹配名称的第一个子节点对象
Element element(String elementName);

- 获取所有的子节点
List<Element> elements();

- 直接获取匹配名称的第一个子节点的文本内容
String elementText(String elementName);

- 获取节点的属性值
String attributeValue(String attributeName);

案例1 - 解析本地xml:

    //1.    创建一个指向XML文档的输入流
InputStream is = new FileInputStream("C:\\code\\33\\code1\\day02_XML\\src\\books.xml");
//2. 创建SAXReader
SAXReader sr = new SAXReader();
//3. 读取XML文档输入流, 得到文档对象
Document doc = sr.read(is);
//4. 通过文档对象, 获取根节点
Element root = doc.getRootElement();
//5. 通过根节点, 获取所有子节点(四个book)

List<Element> es = root.elements();
//6. 循环遍历集合, 得到一个个的book
for (Element book : es) {
//6.1 获取属性
String id = book.attributeValue("id");
String name = book.elementText("name");
String info = book.elementText("info");
System.out.println("图书编号:"+id+"\t图书名称:"+name+"\t图书简介:"+info);
}

案例2 - 解析网络xml

    System.out.println("请输入要查询的手机号码:");
Scanner input = new Scanner(System.in);
String phone = input.nextLine();
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();

/*
* //1. 把xml的输入流, 转换为 字符缓冲读取流 BufferedReader br = new BufferedReader(new
* InputStreamReader(is, "UTF-8")); System.out.println(br.readLine());
* System.out.println(br.readLine());
*/
SAXReader sr = new SAXReader();
Document doc = sr.read(is);
Element root = doc.getRootElement();
//通过根节点, 获取resultCode返回码., 200表示成功
String resultcode = root.elementText("resultcode");
if("200".equals(resultcode)) {
System.out.println("查询成功");
//获取节点result
Element result = root.element("result");
String s1 = result.elementText("province");
String s2 = result.elementText("city");
String s3 = result.elementText("areacode");
String s4 = result.elementText("zip");
String s5 = result.elementText("company");
System.out.println("查询结果如下:");
if(!s1.equals(s2)) {
System.out.println("省份:"+s1);
}
System.out.println("城市:"+s2);
System.out.println("区号:"+s3);
System.out.println("邮编:"+s4);
System.out.println("运营商:"+s5);
}else {
System.out.println("查询失败");
}

XPATH 解析xml 了解

通过路径快速的查找一个或一组元素.

路径表达式:
1. / : 表示从根节点查找子元素
例如: /name , 表示查找根节点中的子节点name节点.

2. // : 从当前节点, 查找后代元素
例如: //name , 表示从发起查找的元素位置, 查找后代元素 name

3. . : 查找当前节点
4. .. : 查找父节点
5. @ : 选择属性

怎么使用路径表达式:

可以通过Node类的方法 来完成路径表达式的使用.


方法1.
一次用于查询单个节点:
Node node = node.selectSingleNode(String "路径表达式");
方法2.
一次用于查询一组节点:
List<Node> node = node.selectNodes(String "路径表达式");


案例:

public static void main(String[] args) throws Exception {
System.out.println("请输入要查询的手机号码:");
Scanner input = new Scanner(System.in);
String phone = input.nextLine();
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();

/*
* //1. 把xml的输入流, 转换为 字符缓冲读取流 BufferedReader br = new BufferedReader(new
* InputStreamReader(is, "UTF-8")); System.out.println(br.readLine());
* System.out.println(br.readLine());
*/
SAXReader sr = new SAXReader();
Document doc = sr.read(is);

Element e = (Element) doc.selectSingleNode("//city");
System.out.println(e.getText());

}


案例2.
//1. 创建一个指向XML文档的输入流
InputStream is = new FileInputStream("C:\\code\\33\\code1\\day02_XML\\src\\book2.xml");
//2. 创建SAXReader
SAXReader sr = new SAXReader();
//3. 读取XML文档输入流, 得到文档对象
Document doc = sr.read(is);
List<Element> names = doc.selectNodes("/max//books//book[@id='2']//name");

for (Element e : names) {
System.out.println(e.getText());
}

通过Java生成XML文档. 了解

步骤:
1. 通过文档帮助器(DocumentHelper), 来创建新的文档对象(Document)
Document doc = DocumentHelper.createDocument();

2. 通过文档对象, 添加根节点:
Element root = doc.addElement("根节点名称");

3. 通过根节点, 丰富子节点
Element 子节点 = root.addElement("子节点名称");

4. 创建一个文件的输出流 , 用于将XML文档, 输出到文件中
FileOutputStream fos = new FileOutputStream("文件保存的路径");
5. 将文件字节输出流, 封装为XML输出流
XMLWriter xw = new XMLWriter(fos);
6. 输出文档, 并关闭流
xw.write(doc);
xw.close();
fos.close();


在上述的操作中, 有三个操作 需要注意:

1. 如何创建新的空文档对象
通过文档帮助器(DocumentHelper), 来创建新的文档对象(Document)
代码: Document doc = DocumentHelper.createDocument();

2. 如何添加子元素:
通过节点, 丰富子节点
代码: Element 子节点 = element.addElement("子节点名称");

3. 如何添加属性:
设置节点的属性
代码: book.addAttribute(String 属性名, String 属性值);

通过xstram 快捷生成XML文档. 了解

步骤1.    创建XStream 对象
XStream x = new XStream();
[步骤2. 修改某个类 ,在XML字符串中的节点名称]
x.alias("book",Book.class);
步骤3. 生成XML文档
String xml字符串 = x.toXML(对象);

案例:
ArrayList<Book> data = new ArrayList<>();
for(int i=0;i<10;i++) {
Book book = new Book(1+i, "金苹果"+i, "从前有座山, 山上有座庙, 庙里有个老和尚和小和尚, 老和尚正在对小和尚说: 嘿嘿嘿");
data.add(book);
}

//步骤1. 创建XStream 对象
XStream x = new XStream();
x.alias("book", Book.class);
//步骤2. 生成XML文档
String str = x.toXML(data);
System.out.println(str);


--------------------------------------------------------------

JDBC 事务 ***

在dos命令行操作oracle时 , 执行DML , 需要结束事务 (commit提交 或 rollback回退)
在JDBC中, 事务是自动提交的, 每执行一条DML语句, 事务就自动提交一次.

我们可以通过JDBC的事务API , 开始事务的手动提交, 将多条DML语句看作一个整体, 要么一起成功, 要么一起失败.

JDBC事务操作格式:

注意: 开启事务的手动提交 ,是通过连接对象完成的.
某个数据连接对象的事务开启手动提交后, 这个连接对象的事务需要手动控制. 其他连接对象不受影响.

操作方法:
1. 开始事务的手动提交:
conn.setAutoCommit(boolean flag);
参数含义: true表示自动提交 . false表示手动提交.

2. 提交事务:
conn.commit();

3. 回退事务:
rollback();

事务案例:

    public class Demo {
public static void main(String[] args) throws Exception {
//1. 加载数据库的驱动
Class.forName("oracle.jdbc.OracleDriver");
//2. 获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "system", "123");
//2.1 设置连接对象的事务 为 手动提交
conn.setAutoCommit(false);
//3. 开始描述逻辑
System.out.println("金刚: 转账中...");
//3.1 减少金刚账户的余额 500 | 3.1.1 预编译SQL执行环境
PreparedStatement state = conn.prepareStatement("update user33 set money=500 where id=2");
//3.1.2 执行SQL语句
boolean success = state.executeUpdate()>0?true:false;
if(success) {
System.out.println("后台逻辑: 金刚余额减少完毕.");
if(1==2) {
conn.rollback();
throw new RuntimeException("后台服务器... 停电了");
}
//3.2 增加豪杰账户的余额 500
//3.2.1 预编译SQL执行环境
PreparedStatement state2 = conn.prepareStatement("update user33 set money=600 where id=1");
//3.2.2 执行SQ语句
boolean success2 = state2.executeUpdate()>0?true:false;
if(success2) {
System.out.println("后台逻辑: 豪杰余额增加完毕");
conn.commit();
}
state2.close();
}
state.close();
}
}

批处理 了解

将多条SQL语句 放到一起批量处理.

批处理将多次对于数据库的操作次数 , 减少到了一次 ! 提高了大量SQL语句一起执行时的性能.

使用步骤:

批处理使用Statement类操作
步骤1. 将一条SQ语句加入到批处理中
statement.addBatch(String sql);

步骤2. 执行批处理中的所有语句
statement.executeBatch();

批处理案例:

    //1.    加载数据库的驱动
Class.forName("oracle.jdbc.OracleDriver");
//2. 获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE",
"system", "123");
//3. 创建SQL的执行环境
Statement state = conn.createStatement();
//4. 加入SQL语句 到批处理中
for(int i=0;i<1000;i++) {
state.addBatch("insert into user33 values(SEQ_USER33_ID.NEXTVAL,'name"+i+"',1000)");
}
//5. 执行批处理
state.executeBatch();
state.close();
conn.close();
System.out.println("执行完毕");

连接池 *

概述 熟悉

由连接池创建连接, 维护连接
我们需要使用连接时, 从连接池中获取连接.
如果池中存在空闲连接, 则拿去使用.
如果不存在空闲连接, 且池未满 , 则在连接池中创建新的连接使用.
如果不存在空闲连接, 且池已满 , 则排队等待空闲连接.

Properties 文件 与 类 熟悉

properties文件 常用于Java中的配置文件.
因为Properties文件 可以快速的 与 Properties类 进行转换.

文件:
注释: #开头表示注释行
键值对: 键与值之间使用等号连接, 多个键值对之间使用换行分割

如何将一个Properties文件, 转换为java中的Map集合对象:

步骤:
1. 创建Properties对象
Properties ppt = new Properties();
2. 得到Properties文件的字节输入流
InputStream is = //可以通过new FileInputStream , 也可以通过ClassLoader 等等
3. 将流加载到Properties对象
ppt.load(is);

使用步骤: *

1.  引入相关的jar文件
- dbcp : 连接池的代码
- poll : 连接池的依赖库

2. 创建一个properties文件, 描述连接池的配置 , 内容如下:
#数据库连接地址
url=jdbc:oracle:thin:@localhost:1521:XE
#数据库驱动地址
driverClassName=oracle.jdbc.OracleDriver
#数据库帐号
username=system
#数据库密码
password=123

#扩展配置:
#初始化连接池时, 创建的连接数量:
initialSize=5
#最大允许存在的连接数量
maxActive=200
#空闲时允许保留的最大连接数量
maxIdle=10
#空闲时允许保留的最小连接数量
minIdle=5
#排队等候的超时时间
maxWait=20000

3. 将properties文件, 转换为Properties对象.
Properties ppt = new Properties();
ppt.load(文件输入流);
4. 通过连接池工厂类(BasicDataSourceFactory) , 创建连接池对象 (一次程序启动, 创建一个连接池就够了.)
DataSource ds = BasicDataSourceFactory.createDataSource(ppt);
5. 通过连接池对象, 获取池中的连接
Connection conn = ds.getConnection();
6. 正常JDBC操作

连接池案例:*

    //3.    将properties文件 转换为Properties对象
Properties ppt = new Properties();
//4. 加载文件的输入流
InputStream is = Demo.class.getClassLoader().getResourceAsStream("dbcp.properties");
//空指针异常
ppt.load(is);
//5. 通过工厂类, 创建连接池
DataSource ds = BasicDataSourceFactory.createDataSource(ppt);
//6. 通过连接池, 获取其中的连接 , 并使用
Connection conn = ds.getConnection();
//正常的JDBC操作
PreparedStatement state = conn.prepareStatement
("insert into user33 values(seq_user33_id.nextval,'嘿嘿嘿',188)");
int count = state.executeUpdate();
System.out.println(count>0?"数据插入成功":"数据插入失败");

DBCPUtil工具类 *

public class DBCPUtil {

private static DataSource dataSource;

static {
//在类加载时, 读取配置文件, 配置连接池
//1. 创建Properites对象
Properties ppt = new Properties();
//2. 读取配置文件,
InputStream is = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
//3. 将配置文件 加载到Properties对象中
try {
ppt.load(is);
//4. 通过连接池工厂类, 创建连接池
dataSource = BasicDataSourceFactory.createDataSource(ppt);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 用于从连接池中 获取一个连接对象
* @return 连接对象 , 如果获取失败返回null
*/
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 用于释放资源
* @param conn 连接对象
* @param state 执行环境
* @param result 结果集
*/
public static void close(Connection conn , Statement state ,ResultSet result) {
if(result!=null) {
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state!=null) {
try {
state.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}

数据库优化 *

1.  在进行表格查询时 , where子句中的条件执行顺序是从左至右 , 清除数据量较大的条件应该放在左边.(特别注意: 笛卡尔积消除条件必须放在最左边)

2. 在进行表格查询时 , 列名列表应避免使用*号 ! 数据库在执行查询操作时, 会先将*号展开, 转换为所有的列名, 再进行查询.

3. 在进行表格查询时 , 能使用where条件筛选的数据, 应尽量避免使用having子句来筛选. 因为where条件执行在having之前 , 在早期筛选掉大量数据, 可以让程序执行的更顺畅.

4. 在进行多表查询时 , 查询的表顺序是从右至左的. 应把表中数据量最少的表放在查询的最右边.

5. 在进行多表查询时 , 应尽可能的给所有的表添加别名, 能明确的区分有冲突的列.

6. 在使用事务时 , 应尽量多的commit , 尽量早的commit ! 原因是: 事务在未提交时, 数据库会耗费大量的内存 , 来缓存未提交的SQL结果 !

7. 尽可能多的使用函数 来提高SQL执行的效率.

8. SQL语句编写时, 除字符串以外 , 应使用大写字母 ! 因为SQL语句执行时, 会先将小写字母 转换为 大写字母, 再执行.

9. 应尽可能少的访问数据库 (多次数据访问的结果可能相同, 如果缓存起来 ,可以提高程序的执行效率)

10. 在索引列上 , 尽可能避免使用not来判断. not关键字如果判断了索引列 , 会导致此次查询索引失效 , 转而使用全表扫描的方式查询.

11. 在索引列上, 不能使用算数运算 , 算数运算也会导致索引列使用, 使用全表扫描的方式进行查询.

12. 在查询数据时, 如果需要使用>或<的条件, 应替换为>= 或 <= !
原因是>和<符号 , 查询时, 是按照>= 和 <= 进行查询, 然后在撇去=的结果.



以上是关于Java与XML 连接池-JDBC 笔记的主要内容,如果未能解决你的问题,请参考以下文章

Java 微服务 乐优网络商城 day02 源代码 SpringBoot 实战开发 整合JDBC和事务(数据库连接池)

java sql数据连接池的问题?

[原创]java WEB学习笔记80:Hibernate学习之路--- hibernate配置文件:JDBC 连接属性,C3P0 数据库连接池属性等

java数据库连接池的介绍与定义一个简单的连接池

JAVA基础:JDBC的使用 附详细代码

数据库连接池的Java连接池