一:JDBC
1.JDBC 概述
-
JDBC(Java Data Base Connectivity) 是 Java 访问数据库的标准规范.是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范.
-
JDBC 原理: JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
-
JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库
驱动jar包, 我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。
2.JDBC 开发
- 1.加载驱动:Class.forName(数据库驱动实现类)
- 2.获取连接:使用 DriverManager类的静态方法getConnection方法,获取数据库的连接
- 3.获取Statement对象
- 4.处理结果集:只有在进行查询操作的时候, 才会处理结果集ResultSet
- 5.释放资源:先开的后关,后开的先关,ResultSet ==> Statement ==> Connection
3.SQL注入问题
- SQL注入: 我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有SQL 真正的意义,以上问题称为 SQL注入。
# SQL注入演示 -- 填写一个错误的密码 SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1'; 相当于 select * from user where true=true; 查询了所有记录 如果这是一个登陆操作,那么用户就登陆成功了.显然这不是我们想要看到的结果
- 要解决 SQL 注入,就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。
4.预处理对象
-
PreparedStatement 是Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的SQL语句对象.
-
预编译: 是指SQL语句被预编译,并存储在PreparedStatement对象中。然后可以使用此对象多次高效地执行该语句。
-
PreparedStatement特点: 因为有预先编译的功能,提高SQL的执行效率。可以有效的防止 SQL 注入的问题,安全性更高
-
Statement 与 PreparedStatement的区别
- 1.Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
- 2.PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
- 3.PrepareStatement可以减少编译次数提高数据库性能。
5.JDBC 控制事务
- 使用Connection中的方法实现事务管理
- void setAutoCommit(boolean autoCommit):参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务
- void commit():提交事务
- void rollback():回滚事务
- 开发步骤
- 1.获取连接
- 2.开启事务
- 3.获取到 PreparedStatement , 执行两次更新操作
- 4.正常情况下提交事务
- 5.出现异常回滚事务
- 6.最后关闭资源
public class TestJDBCTransaction {
//使用JDBC操作事务
public static void main(String[] args) {
Connection con = null;
PreparedStatement ps = null;
try {
//1.获取连接
con = JDBCUtils.getConnection();
//2.开启事务
con.setAutoCommit(false); //手动提交事务
//3.获取预处理对象 执行SQL (两次修改操作)
//3.1 tom账户 - 500
ps = con.prepareStatement("update account set money = money - ? where name = ?");
ps.setDouble(1,500.0);
ps.setString(2,"tom");
ps.executeUpdate();
//模拟 tom转账之后出现异常
System.out.println(1 / 0);
//3.2 jack账户 + 500
ps = con.prepareStatement("update account set money = money + ? where name = ?");
ps.setDouble(1,500.0);
ps.setString(2,"jack");
ps.executeUpdate();
//4.提交事务 (正常情况)
con.commit();
System.out.println("转账成功! !");
} catch (SQLException e) {
e.printStackTrace();
//5.出现异常就回滚事务
try {
con.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//6.释放资源
JDBCUtils.close(con,ps);
}
}
}
二:数据库连接池和DBUtils
-
连接池: 实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池.
-
连接池的好处: 用池来管理Connection,这样可以重复使用Connection。 当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。
-
普通 JDBC方式:
-
连接池方式:
-
Java为数据库连接池提供了公共的接口: javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!常见的连接池有 DBCP连接池, C3P0连接池, Druid连接池,
-
DBCP 是一个开源的连接池,是Apache成员之一,在企业开发中也比较常见,tomcat内置的连接池。
- 常见配置项:
- driverClassName: 数据库驱动名称
- url: 数据库地址
- username: 用户名
- password: 密码
- maxActive: 最大连接数量
- maxIdle: 最大空闲连接
- minIdle: 最小空闲连接
- initialSize: 初始化连接
/**
* DBCPUtils 工具类
*/
public class DBCPUtils {
//1.定义常量 保存数据库连接的相关信息
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/lagou_jy?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "root";
//2.创建连接池对象 (有DBCP提供的实现类)
public static BasicDataSource dataSource = new BasicDataSource();
//3.使用静态代码块进行配置
static{
dataSource.setDriverClassName(DRIVERNAME);
dataSource.setUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaxActive(20);
}
//4.获取连接的方法
public static Connection getConnection() throws SQLException {
//从连接池中获取连接
Connection connection = dataSource.getConnection();
return connection;
}
//5.释放资源方法
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//5.释放资源方法
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
-
C3P0 是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。
配置文件c3p0-config.xml,文件名不可更改,直接放到src下,也可以放到到资源文件夹中 <c3p0-config> <!--默认配置--> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/lagou_jy?characterEncoding=UTF-8</property> <property name="user">root</property> <property name="password">root</property> <!-- initialPoolSize:初始化时获取三个连接, 取值应在minPoolSize与maxPoolSize之间。 --> <property name="initialPoolSize">3</property> <!-- maxIdleTime:最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。--> <property name="maxIdleTime">60</property> <!-- maxPoolSize:连接池中保留的最大连接数 --> <property name="maxPoolSize">100</property> <!-- minPoolSize: 连接池中保留的最小连接数 --> <property name="minPoolSize">10</property> </default-config> <!--配置连接池mysql--> <named-config name="mysql"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/lagou_jy</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">10</property> <property name="maxIdleTime">30</property> <property name="maxPoolSize">100</property> <property name="minPoolSize">10</property> </named-config> <!--配置连接池2,可以配置多个--> </c3p0-config> /** * C3P0提供的核心工具类, ComboPooledDataSource , 如果想使用连接池,就必须创建该类的对象 * new ComboPooledDataSource(); 使用 默认配置 * new ComboPooledDataSource("mysql"); 使用命名配置 */ public class C3P0Utils { //1.创建连接池对象 C3P0对DataSource接口的实现类 //使用的配置是 配置文件中的默认配置 //public static ComboPooledDataSource dataSource = new ComboPooledDataSource(); //使用指定的配置 public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql"); //获取连接的方法 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } //释放资源 public static void close(Connection con, Statement statement){ if(con != null && statement != null){ try { statement.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } } } //释放资源 public static void close(Connection con, Statement statement, ResultSet resultSet){ if(con != null && statement != null && resultSet != null){ try { resultSet.close(); statement.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。
配置文件:druid.properties driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/lagou_jy1?characterEncoding=UTF-8 username=root password=root initialSize=5 maxActive=10 maxWait=3000 Druid工具类: /** * 获取数据库连接池对象 * 通过工厂来来获取 DruidDataSourceFactory类的createDataSource方法 * createDataSource(Properties p) 方法参数可以是一个属性集对象 */ public class DruidUtils { //1.定义成员变量 public static DataSource dataSource; //2.静态代码块 static{ try { //3.创建属性集对象 Properties p = new Properties(); //4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件 InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"); //5. 使用Properties对象的 load方法 从字节流中读取配置信息 p.load(inputStream); //6. 通过工厂类获取连接池对象 dataSource = DruidDataSourceFactory.createDataSource(p); } catch (Exception e) { e.printStackTrace(); } } //获取连接的方法 public static Connection getConnection(){ try { return dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); return null; } } //获取Druid连接池对象的方法 public static DataSource getDataSource(){ return dataSource; } //释放资源 public static void close(Connection con, Statement statement){ if(con != null && statement != null){ try { statement.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } } } //释放资源 public static void close(Connection con, Statement statement, ResultSet resultSet){ if(con != null && statement != null && resultSet != null){ try { resultSet.close(); statement.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
DBUtils工具类: 使用JDBC我们发现冗余的代码太多了,为了简化开发 我们选择使用 DbUtils,Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。(DBUtils就是JDBC的简化开发工具包,需要项目导入commons-dbutils-1.6.jar)
- 步骤:
1.创建QueryRunner(手动或自动)
2.占位符方式 编写SQL
3.设置占位符参数
4.执行
- Dbutils核心功能
- 1.QueryRunner 中提供对sql语句操作的API.
- 2.ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
- 3.DbUtils类,他就是一个工具类,定义了关闭资源与事务处理相关方法
-
QueryRunner核心类
构造方法: QueryRunner() QueryRunner(DataSource ds) ,提供数据源(连接池),DBUtils底层自动维护连接connection 常用方法: update(Connection conn, String sql, Object... params) ,用来完成表数据的增加、删除、更新操作 query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) ,用来完成表数据的查询操作 QueryRunner的创建模式: //手动方式 创建QueryRunner对象 QueryRunner qr = new QueryRunner(); //自动创建 传入数据库连接池对象 QueryRunner qr2 = new QueryRunner(DruidUtils.getDataSource()); //获取连接池对象(自动模式需要传入连接池对象) public static DataSource getDataSource(){ return dataSource; } QueryRunner实现增、删、改操作核心方法: update(Connection conn, String sql, Object... params) Connection conn:数据库连接对象, 自动模式创建QueryRun 可以不传 ,手动模式必须传递 String sql:占位符形式的SQL ,使用 ? 号占位符 Object... param:Object类型的 可变参,用来设置占位符上的参数
-
QueryRunner实现查询操作
- ResultSetHandler接口 可以对查询出来的ResultSet结果集进行处理,达到一些业务上的需求。
- ResultSetHandler接口的几个常见实现类实现数据库的增删改查,可以大大减少代码量,优化程序。每一种实现类都代表了对查询结果集的一种处理方式
ArrayHandler: 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler: 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。
BeanHandler: 将结果集中第一条记录封装到一个指定的javaBean中.
BeanListHandler: 将结果集中每一条记录封装到指定的javaBean中,再将这些javaBean在封装到List集合中
ColumnListHandler: 将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler: 将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。
MapHandler: 将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值
MapListHandler: 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。
ScalarHandler: 它是用于封装单个数据。例如 select count(*) from 表操作。
- QueryRunner的查询方法: query方法的返回值都是泛型,具体的返回值类型,会根据结果集的处理方式,发生变化
query(String sql, handler ,Object[] param) 自动模式创建QueryRunner, 执行查询 query(Connection con,String sql,handler,Object[] param) 手动模式创建QueryRunner, 执行查询
- JavaBean组件: JavaBean就是一个类, 开发中通常用于封装数据,创建实体类和数据库的表对应。
- 特点:
- 需要实现 序列化接口, Serializable (暂时可以省略)
- 提供私有字段: private 类型 变量名;
- 提供 getter 和 setter
- 提供 空参构造
数据库批处理
- 批处理(batch) 操作数据库:批处理指的是一次操作中执行多条SQL语句,批处理相比于一次一次执行效率会提高很多。当向数据库中添加大量的数据时,需要用到批处理。
- 举例: 送货员的工作,未使用批处理的时候,送货员每次只能运送 一件货物给商家;使用批处理,则是送货员将所有要运送的货物, 都用车带到发放处派给客户。
- 实现批处理: Statement和PreparedStatement都支持批处理操作,
要用到的方法: void addBatch():将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中。通过调用方法 executeBatch 可以批量执行此列表中的命令。 int[] executeBatch():每次提交一批命令到数据库中执行,如果所有的命令都成功执行了,那么返回一个数组,这个数组是说明每条命令所影响的行数 mysql 批处理是默认关闭的,所以需要加一个参数才打开mysql 数据库批处理,在url中添加 rewriteBatchedStatements=true 例如: url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&rewriteBatchedStatements=true
MySql元数据
- 元数据: 除了表之外的数据都是元数据,可以分为三类
- 查询结果信息: UPDATE 或 DELETE语句 受影响的记录数。
- 数据库和数据表的信息: 包含了数据库及数据表的结构信息。
- MySQL服务器信息: 包含了数据库服务器的当前状态,版本号等。
元数据相关的命令介绍
-- 1.查看服务器当前状态
show status;
-- 2.mysql服务器的版本信息
select version();
-- 3.查询表中的详细信息,和desc table_name一样
show columns from table_name;
-- 4.显示数据表的详细索引信息,包括PRIMARY KEY(主键)
show index from table_name;
-- 5.列出所有数据库
show databases;
-- 6.显示当前数据库的所有表
show tables;
-- 7.获取当前的数据库名
select database();
- 使用JDBC 获取元数据: 通过JDBC 也可以获取到元数据,比如数据库的相关信息,或者当我们使用程序查询一个不熟悉的表时, 我们可以通过获取元素据信息,了解表中有多少个字段,字段的名称 和 字段的类型
JDBC中描述元数据的类 DatabaseMetaData: 描述数据库的元数据对象 ResultSetMetaData: 描述结果集的元数据对象
获取元数据对象的方法 : getMetaData () 1.connection 连接对象, 调用 getMetaData () 方法,获取的是DatabaseMetaData 数据库元数据对象 2.PrepareStatement 预处理对象调用 getMetaData () , 获取的是ResultSetMetaData , 结果集元数据对象 DatabaseMetaData的常用方法: getURL() : 获取数据库的URL getUserName(): 获取当前数据库的用户名 getDatabaseProductName(): 获取数据库的产品名称 getDatabaseProductVersion(): 获取数据的版本号 getDriverName(): 返回驱动程序的名称 isReadOnly(): 判断数据库是否只允许只读 true 代表只读
ResultSetMetaData的常用方法: getColumnCount() : 当前结果集共有多少列 getColumnName(int i) : 获取指定列号的列名, 参数是整数 从1开始 getColumnTypeName(int i): 获取指定列号列的类型, 参数是整数 从1开始
三:XML
- XML即可扩展标记语言(Extensible Markup Language):W3C在1998年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本 !
- 特点: 可扩展的, 标签都是自定义的,语法十分严格
- XML的作用:
- 存储数据: 通常我们在数据库中存储数据。不过,如果希望数据的可移植性更强,我们可以把数据存储 XML 文件中
- 配置文件: 作为各种技术框架的配置文件使用 (最多)
- 在网络中传输: 客户端可以使用XML格式向服务器端发送数据,服务器接收到xml格式数据,进行解析
XML的语法
- XML文档声明格式: 文档声明必须为结束,文档声明必写在第一行;
语法格式: <?xml version="1.0" encoding="UTF-8"?> 属性说明: versioin:指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0; encoding:指定当前文档的编码。可选属性,默认值是utf-8;
- 元素: Element 元素,是XML文档中最重要的组成部分
元素的命名规则: 1.不能使用空格,不能使用冒号 2.xml 标签名称区分大小写 3.XML 必须有且只有一个根元素 语法格式: <users><users> XML 必须有且只有一个根元素,它是所有其他元素的父元素,比如以下实例中 users 就是根元素: <?xml version="1.0" encoding="utf-8" ? <users</users> 普通元素的结构开始标签、元素体、结束标签组成 <hello大家好 </hello> 元素体:元素体可以是元素,也可以是文本 <hello <a>你好</a </hello> 空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合 <close/>
- 属性
- 属性是元素的一部分,它必须出现在元素的开始标签中
- 属性的定义格式:属性名=属性值,其中属性值必须使用单引或双引
- 一个元素可以有0~N个属性,但一个元素中不能出现同名属性
- 属性名不能使用空格、冒号等特殊字符,且必须以字母开头
<bean id="" class=""</bean>
-
注释: XML的注释,以“ ”结束。注释内容会被XML解析器忽略!
-
使用XML 描述数据表中的数据
<?xml version="1.0" encoding="UTF-8" ?> <employees> <employee eid="2"> <ename>林黛玉</ename> <age>20</age <sex>女</sex <salary>5000</salary> <empdate>2019-03-14</empdate> </employee> <employee eid="3"> <ename>杜甫</ename> <age>40</age> <sex>男</sex> <salary>15000</salary> <empdate>2010-01-01</empdate> </employee> </employees>
-
XML约束: 在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
- 常见的xml约束:DTD,Schema
- 作为程序员只要掌握两点:会阅读,会引入,不用自己编写
-
DTD约束(Document Type Definition):文档类型定义,用来约束XML文档。 规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。,开发中,我们不会自己编写DTD约束文档,通常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。常见框架使用DTD约束有:
Struts2、hibernate等。创建约束文件 student.dtd <!ELEMENT students (student+) > <!ELEMENT student (name,age,sex)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ELEMENT sex (#PCDATA)> <!ATTLIST student number ID #REQUIRED> <!-- ELEMENT: 用来定义元素 students (student+) : 代表根元素 必须是 <students> student+ : 根标签中至少有一个 student子元素, + 代表至少一个 student (name,age,sex): student 标签中包含的子元素,按顺序出现 #PCDATA: 是普通文本内容 ATTLIST: 用来定义属性 student number ID #REQUIRED student子元素中 有一个ID属性叫做 number,是必须填写的 ID: 唯一 值只能是字母或者下划线开头 --> 引入DTD:引入dtd文档到xml文档中,两种方式 内部dtd:将约束规则定义在xml文档中 外部dtd:将约束的规则定义在外部的dtd文件中 本地: 网络: student.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE students SYSTEM "student.dtd"> <students> <student number="s1"> <name>小斌</name> <age>22</age> <sex>男</sex> </student> <student number="s2"> <name>广坤</name> <age>55</age> <sex>男</sex> </student> </students>
-
Schema约束:
- Schema是新的XML文档约束, 比DTD强大很多,是DTD 替代者;
- Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。
- Schema 功能更强大,内置多种简单和复杂的数据类型
- Schema 支持命名空间 (一个XML中可以引入多个约束文档)
-
Schema约束示例
student.xsd <?xml version="1.0"?> <xsd:schema xmlns="http://www.lagou.com/xml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.lagou.com/xml" elementFormDefault="qualified"> <xsd:element name="students" type="studentsType"/> <xsd:complexType name="studentsType"> <xsd:sequence> <xsd:element name="student" type="studentType" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="studentType"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="age" type="ageType" /> <xsd:element name="sex" type="sexType" /> </xsd:sequence> <xsd:attribute name="number" type="numberType" use="required"/> </xsd:complexType> <xsd:simpleType name="sexType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="male"/> <xsd:enumeration value="female"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="ageType"> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="0"/> <xsd:maxInclusive value="200"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="numberType"> <xsd:restriction base="xsd:string"> <xsd:pattern value="hehe_\d{4}"/> </xsd:restriction> </xsd:simpleType> </xsd:schema>
-
XML引入Schema约束
student.xml xml中引入schema约束的步骤: <?xml version="1.0" encoding="UTF-8" ?> <-- 1.查看schema文档,找到根元素<students> </students>,在xml中写出来 --> <-- 2.根元素来自哪个命名空间。使用xmlns指令来声明 --> <students xmlns="http://www.lagou.com/xml" <-- 3.引入 w3c的标准命名空间, 复制即可 --> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <-- 4.引入的命名空间跟哪个xsd文件对应:使用schemaLocation来指定 两个取值: 第一个为命名空间 第二个为xsd文件的路径 命名空间:指的是一个环境,所用的标签来自于哪个环境定义的。 --> xsi:schemaLocation="http://www.lagou.com/xml student.xsd" > <student number="hehe_1234"> <name>张百万</name> <age>25</age> <sex>female</sex> </student> <student number="hehe_0000"> <name>小斌</name> <age>20</age> <sex>male</sex> </student> </students>
XML 解析
-
概述: 当将数据存储在XML后,我们就希望通过程序获得XML的内容。如果我们使用Java基础所学习的IO知识是可以完成的,不过你需要非常繁琐的操作才可以完成,且开发中会遇到不同问题(只读、读写)。人们为不同问题提供不同的解析方式,并提交对应的解析器,方便开发人员操作XML。
-
XML解析方式: 开发中比较常见的解析方式有两种
DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。
优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
缺点:XML文档过大,可能出现内存溢出显现。
**SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。**并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)
优点:占用内存少 处理速度快,可以处理大文件
缺点:只能读,逐行后将释放资源。
- XML常见的解析器: 解析器,就是根据不同的解析方式提供的具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包
- JAXP: sun公司提供的解析器,支持DOM和SAX两种思想
- DOM4J: 一款非常优秀的解析器 , Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
- Jsoup: jsoup 是一款Java 的HTML解析器 ,也可以解析XML
- PULL: Android内置的XML解析方式,类似SAX。
dom4j 的使用
- 使用核心类SaxReader加载xml文档获得Document,通过Document 对象获得文档的根元素,然后就可以操作了。
常用API如下: SaxReader对象: read(…) 加载执行xml文档 Document对象: getRootElement() 获得根元素 Element对象: elements(…) 获得指定名称的所有子元素。可以不指定名称 element(…) 获得指定名称的第一个子元素。可以不指定名称 getName() 获得当前元素的元素名 attributeValue(…) 获得指定属性名的属性值 elementText(…) 获得指定名称子元素的文本值 getText() 获得当前元素的文本内容
- 1.导入JAR包
- 2.准备xml文件
编写user.xsd schema约束
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.lagou.com/xml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lagou.com/xml" elementFormDefault="qualified">
<xsd:element name="users" type="usersType"/>
<xsd:complexType name="usersType">
<xsd:sequence>
<xsd:element name="user" type="userType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="userType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="ageType" />
<xsd:element name="hobby" type="hobbyType" />
</xsd:sequence>
<xsd:attribute name="id" type="numberType" use="required"/>
</xsd:complexType>
<xsd:simpleType name="ageType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="hobbyType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="抽烟"/>
<xsd:enumeration value="喝酒"/>
<xsd:enumeration value="烫头"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="numberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
编写user.xml 引入约束
<?xml version="1.0" encoding="UTF-8" ?>
<users
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lagou.com/xml user.xsd">
<user id="001">
<name>张百万</name>
<age>25</age>
<hobby>抽烟</hobby>
</user>
<user id="002">
<name>于谦</name>
<age>55</age>
<hobby>烫头</hobby>
</user>
<user id="003">
<name>小斌</name>
<age>25</age>
<hobby>喝酒</hobby>
</user>
</users>
- 3.读取XML
public class TestDOM4j {
//获取XML文件中的 所有的元素名称(标签)
@Test
public void test1() throws DocumentException {
//1.获取XML解析对象
SAXReader reader = new SAXReader();
//2.解析XML 获取 文档对象 document
Document document = reader.read("src\\com\\lagou\\xml03\\user.xml");
//3.获取根元素
Element rootElement = document.getRootElement();
//获取根元素名称
System.out.println(rootElement.getName());
//获取 根元素下的标签
List<Element> elements = rootElement.elements();
for (Element element : elements) {
System.out.println("根标签下的子节点: " + element.getName());
List<Element> eList = element.elements();
for (Element e : eList) {
System.out.println("user标签下的子节点" + e.getName());
}
break;
}
}
//获取具体的节点内容,XML中标签的文本信息 和 属性信息
@Test
public void test2() throws DocumentException {
//1.获取解析XML的 SAXReader
SAXReader reader = new SAXReader();
//2.获取文档对象
Document document = reader.read("src\\com\\lagou\\xml03\\user.xml");
//3.获取根节点
Element rootElement = document.getRootElement();
//4.获取子节点 user
List<Element> elements = rootElement.elements();
//5.获取集合中的第一个 子节点
Element user = elements.get(0);
//6.获取节点中的文本信息
String id = user.attributeValue("id");//获取属性 id的值
String name = user.elementText("name");
String age = user.elementText("age");
String hobby = user.element("hobby").getText(); //使用getText获取当前元素的文本内容
//打印
System.out.println(id + " " + name +" " + age + " " + hobby);
}
}
xpath方式读取xml
- XPath 是一门在 XML 文档中查找信息的语言。可以是使用xpath查找xml中的内容。
- XPath的好处: 由于DOM4J在解析XML时只能一层一层解析,所以当XML文件层数过多时使用会很不方便,结合XPATH就可以直接获取到某个元素.(需导入 jaxen-1.1-beta-6.jar)
使用dom4j支持xpath的操作的几种主要形式:
/AAA/DDD/BBB: 表示一层一层的,AAA下面 DDD下面的BBB
//BBB: 表示和这个名称相同,表示只要名称是BBB,都得到
//* :所有元素
BBB[1] , BBB[last()]:第一种表示第一个BBB元素, 第二种表示最后一个BBB元素
//BBB[@id]: 表示只要BBB元素上面有id属性,都得到
//BBB[@id='b1']: 表示元素名称是BBB,在BBB上面有id属性,并且id的属性值是b1
常用方法:
selectSingleNode(query): 查找和 XPath 查询匹配的一个节点。
参数是Xpath 查询串。
selectNodes(query): 得到的是xml根节点下的所有满足 xpath 的节点;
参数是Xpath 查询串。
Node: 节点对象
-
Xpath读取XML
数据准备 book.xml <?xml version="1.0" encoding="UTF-8" ?> <bookstore> <book id="book1"> <name>金瓶梅</name> <author>金圣叹</author> <price>99</price> </book> <book id="book2"> <name>红楼梦</name> <author>曹雪芹</author> <price>69</price> </book> <book id="book3"> <name>Java编程思想</name> <author>埃克尔</author> <price>59</price> </book> </bookstore> public class TestXPath { /* * 使用selectSingleNode() 方法 查询自定的节点信息 * * */ @Test public void test1() throws DocumentException { //1.创建XML解析对象 SAXReader reader = new SAXReader(); //2.解析XML 获取 文档对象 Document document = reader.read("src\\com\\lagou\\xml04\\book.xml"); //3.通过selectSingleNode() 方法获取 name节点 Node node1 = document.selectSingleNode("/bookstore/book/name"); System.out.println("节点的名称: " + node1.getName()); System.out.println("书名: " + node1.getText()); //4.获取第二本书的书名 Node node2 = document.selectSingleNode("/bookstore/book[3]/name"); System.out.println("书名: " + node2.getText()); } /* * 使用 selectSIngleNode() 方法获取 属性值 或者 通过属性值获取到节点信息 * * */ @Test public void test2() throws DocumentException { SAXReader reader = new SAXReader(); Document document = reader.read("src\\com\\lagou\\xml04\\book.xml"); //1.获取第一个booke节点中的 id属性的值 Node node1 = document.selectSingleNode("/bookstore/book/attribute::id"); System.out.println("第一个book的id属性值: " + node1.getText() ); //2.获取最后一个book节点的 id属性值 Node node2 = document.selectSingleNode("/bookstore/book[last()]/attribute::id"); System.out.println("最后一个book节点中的属性值: " + node2.getText()); //3.通过id的值 获取book2节点 中的书名 Node node3 = document.selectSingleNode("/bookstore/book[@id='book2']"); String name = node3.selectSingleNode("name").getText(); System.out.println("id为book2的 节点的书名是: " + name); } /* * 使用selectNodes() 获取所有指定名称 的节点 * * */ @Test public void test3() throws DocumentException { SAXReader reader = new SAXReader(); Document document = reader.read("src\\com\\lagou\\xml04\\book.xml"); //1.查询所有的节点 List<Node> list = document.selectNodes("//*"); for (Node node : list) { System.out.println("节点名: " + node.getName()); } //2.获取所有的书名 List<Node> list1 = document.selectNodes("//name"); for (Node node : list1) { System.out.println("书名: " + node.getText()); } //3.获取 id值为 book1 的节点中的所有内容 List<Node> list2 = document.selectNodes("/bookstore/book[@id='book1']//*"); for (Node node : list2) { System.out.println(node.getName() + " = " + node.getText()); } } }
JDBC自定义XML
创建自定义jdbc-config.xml 文件, 保存 数据库连接信息
<?xml version="1.0" encoding="UTF-8" ?>
<jdbc>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/lagou_db5?characterEncoding=UTF-8</property>
<property name="user">root</property>
<property name="password">root</property>
</jdbc>
编写工具类 ,使用xpath 读取数据库信息
public class JDBCUtils {
//1.定义字符串变量 保存连接信息
public static String DRIVERNAME;
public static String URL;
public static String USER;
public static String PASSWORD;
//2.静态代码块
static{
//使用 XPath语法 对xml中的数据进行读取
SAXReader reader = new SAXReader();
try {
Document document = reader.read("src\\com\\lagou\\xml05\\jdbc-config.xml");
//1.获取驱动名称
Node driver = document.selectSingleNode("/jdbc/property[@name='driverClass']");
DRIVERNAME = driver.getText();
//2.获取URL
Node url = document.selectSingleNode("/jdbc/property[@name='jdbcUrl']");
URL = url.getText();
//3.获取用户名
Node user = document.selectSingleNode("/jdbc/property[@name='user']");
USER = user.getText();
//4.获取密码
Node password = document.selectSingleNode("/jdbc/property[@name='password']");
PASSWORD = password.getText();
//注册驱动
Class.forName(DRIVERNAME);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection(){
try {
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
return connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
四:综合案例—商城
1.分析设计表关系
2.环境搭建
- 项目结构
- com.lagou.app 测试包 用于对DAO代码进行测试
- com.lagou.dao dao包 数据访问层,包含所有对数据库的相关操作的类
- com.lagou.entity 实体包 保存根据数据库表 对应创建的JavaBean类
- com.lagou.utils 工具包
-
导入所需Jar包
-
导入配置文件及工具类
3.JavaBean类创建
- 在Java一对多的数据关系中,需要遵循以下设计原则:
- Java类的名称 = 实体表的名称
- Java类的属性 = 实体表的字段
- Java类的一个对象 = 表的一行记录
- 外键关系 = 引用配置
- 一个用户拥有多个订单,所以 用户是一的一方, 订单是多的一方