1.1 JDBC:
1.1.1 JDBC的概述:
什么是JDBC:
JDBC:(Java DataBaseConnectivity Java数据库连接).
* 是一种用于执行SQL语句的Java 的API.可以为多种关系型数据库提供统一的访问.它是由一组使用Java语言编写的类或接口组成.
JDBC的功能:
JDBC功能主要就是使用Java语言连接到数据库.
哪些地方使用JDBC:
任意一个应用:
* 学生选课:
* OA:
* 系统都需要与数据库进行交互.都需要使用JDBC.
什么是驱动:
驱动:两个设备之间的通信桥梁.
* Java语言要连接数据库必须使用数据库的驱动.
* 各个数据库的生产商提供数据库的驱动.使用Java连接各种数据库.需要了解各个数据库的驱动.增加Java程序员压力.
* SUN公司与各个数据库生产商协商.由SUN公司提供一套统一的规范.由各个数据库的生产商提供这套规范的实现.
* SUN公司提供了一组接口.各个数据库生产商提供了这套接口的实现.(这组规范就是JDBC规范.)
1.1.2 JDBC的快速入门:
JDBC开发步骤:
步骤1:搭建开发环境,引入数据库的驱动.
步骤2:在程序中加载数据库驱动.
步骤3:获得数据库连接.
步骤4:编写SQL.执行SQL.
步骤5:释放资源.
JDBC的入门案例:
public void demo1() throws SQLException{
// 1.加载驱动
DriverManager.registerDriver(new Driver());
// 2.获得连接.
Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/web017","root", "123");
// 3.编写SQL并且执行SQL.
String sql = "select * from user";
Statement statement = connection.createStatement();
// 结果集:查询后的数据的封装.
ResultSet rs = statement.executeQuery(sql);
// 遍历:
while(rs.next()){
int id = rs.getInt("id");
String username =rs.getString("username");
String password = rs.getString("password");
System.out.println(id+" "+username+" "+password);
}
// 4.释放资源.
rs.close();
statement.close();
connection.close();
}
1.1.3 JDBC的API的详解:
DriverManager:驱动的管理类.
主要的作用:
* 一、注册驱动:(加载驱动)
* registerDriver(Driver driver);
***** 在实际开发中很少使用registerDriver注册驱动?
* 1.使用这个方法注册驱动,使程序依赖了具体的驱动.
* import com.mysql.jdbc.Driver;
* 2.这个方法会导致驱动被注册两次:---查看Driver类的源码.
* 查看源码可以看到:在静态代码块中注册了驱动.
* 静态代码块何时执行?:在类加载的时候.
static {
try {
java.sql.DriverManager.registerDriver(newDriver());
}catch (SQLException E) {
thrownew RuntimeException("Can't register driver!");
}
}
***** 解决这两个问题:
* 反射: Class.forName(“com.mysql.jdbc.Driver”);
* 二、获得连接:
* getConnection(String url,String username,String password);
* url :数据库连接的路径.
* username :连接数据库用户名.
* password :连接数据库密码.
***** url的写法:
* jdbc:mysql://localhost:3306/web017
* jdbc :连接数据库协议.
* mysql :JDBC协议的一个子协议.
* localhost:数据库服务器主机名.
* 3306 :mysql数据库服务器的端口号.
* web017 :数据库名称.
* url简写为 :jdbc:mysql//localhost:3306/web017默认连接本地的mysql服务器默认的端口号3306.
* 简写 :jdbc:mysql:///web017
Connection:连接对象.
Connection由DriverManager创建的.代表的是一个连接的对象.
主要有两个作用:
* 一、创建执行SQL语句的对象.
* 创建Statement对象.
* 创建PreparedStatement对象.对SQL进行预编译.(防止SQL注入的漏洞.)
* 创建CallableStatement.用来执行数据库中存储过程.
* 二、进行事务的管理:
* 设置事务不自动提交.
* 事务提交.
* 事务回滚.
Statement:执行SQL的对象.
Statement对象由Connection对象创建的.代表是可以运行SQL的类.
主要有两个作用:
* 一、执行SQL语句.
* 执行查询语句.执行select语句.返回了一个ResultSet对象.代表查询结果的表格数据.
* 执行更新语句.执行update、insert、delete语句的时候.返回int类型数据.int类型的数据代表的是影响的行数.
* 执行SQL语句.执行select、insert、update、delete语句.返回的是boolean值.如果返回的结果为ResultSet.那么boolean值为true.如果返回的是更新的记录数.那么boolean就为false.
二、执行批处理.
* 将SQL添加到批处理中.
* 清空批处理.
* 执行批处理.
ResultSet:结果集.
ResultSet对象由Statement对象中executeQuery(String sql);返回的.ResultSet对象代表的是select查询后的结果.
* 在ResultSet内部维护了一个指向标哥数据行的游标Cursor,初始化时候,游标在第一行之前的位置.调用ResultSet中next()方法.可以使游标指向具体的数据行,进而调用方法获得该行的数据.
方法:
* 向下移动光标.
* 获得结果集中整形数据.
* 获得结果集中字符串类型的数据:
。。。。。。
...
遍历结果集:
* while(rs.next()){
int id = rs.getInt("id");
String username =rs.getString("username");
String password =rs.getString("password");
System.out.println(id+" "+username+" "+password);
}
获得结果集中的数据:
* int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
如果结果集中只有一条记录:
* if(rs.next()){
...
}
滚动结果集:(了解)
结果集默认的情况下只能向下而且不可以在结果集上进行修改记录.
* MYSQL数据库结果集可以滚动.
* 在Statement被创建的时候:
* Statement createStatement(int resultSetType,intresultSetConcurrency);
* resultSetType :结果集类型
* TYPE_FORWARD_ONLY :结果集只能向下.
* TYPE_SCROLL_INSENSITIVE :结果集可以滚动.不允许进行修改结果集
* TYPE_SCROLL_SENSITIVE :结果集可以滚动.允许在修改结果集.
* resultSetConCurrency :结果集并发策略.
* CONCUR_READ_ONLY :结果集不可以修改
* CONCUR_UPDATABLE :结果集可以修改.
* 组合:
* TYPE_FORWARD_ONLY CONCUR_READ_ONLY :结果集只能向下而且不可以进行修改.(默认值)
* TYPE_SCROLL_INSENSITIVE CONCUR_READ_ONLY:结果集可以滚动,但是不可以修改结果集.
* TYPE_SCROLL_SENSITIVECONCUR_UPDATABLE :结果集可以滚动,而且可以修改结果集.
案例:
public void demo2() throwsClassNotFoundException, SQLException {
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获得连接
Connection conn =DriverManager.getConnection("jdbc:mysql:///web017",
"root","123");
// 3.执行SQL.
String sql = "select* from user";
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs =stmt.executeQuery(sql);
// 直接定位到某一行:
rs.absolute(3);
rs.updateString("username","eee");
rs.updateRow();
// 4.释放资源
rs.close();
stmt.close();
conn.close();
}
1.1.4 JDBC的资源释放:
资源的释放是非常重要,我们写的资源释放的代码,是非常不规范!!!
尤其Connection对象是非常稀有!代表的是数据库连接对象.安装MYSQL的数据库时候,MYSQL有最大连接数量.如果达到MYSQL的最大连接数量,而且Connection都没有被释放.其他人连接不到数据库.
如果Connection不能及时,正确关闭.极易导致系统宕机.Connection的使用原则:尽量要晚创建,尽量早释放!!!
为了确保释放的代码能够得到执行,资源释放的代码一定要放在finally代码块.
资源释放的一段标准代码:
// 4.释放资源.
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null; // 为了让JVM垃圾回收更早回收该对象.
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
1.1.5 JDBC完成CRUD的操作:
参见JDBCDemo2案例.
1.1.6 JDBC工具类的抽取:
public class JDBCUtils{
private static final String DRIVERCLASS;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
static {
// 解析属性文件:Properties.
/* Properties properties = new Properties();
// 获得代表属性文件的输入流:
// 使用类的加载器获得属性文件的输入流:
InputStream is =JDBCUtils.class.getClassLoader().getResourceAsStream(
"jdbc.properties");
// 加载属性文件:
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
DRIVERCLASS = properties.getProperty("jdbc.driverClass");
URL = properties.getProperty("jdbc.url");
USERNAME = properties.getProperty("jdbc.username");
PASSWORD = properties.getProperty("jdbc.password");*/
// 国际化有关:
DRIVERCLASS =ResourceBundle.getBundle("jdbc").getString("jdbc.driverClass");
URL =ResourceBundle.getBundle("jdbc").getString("jdbc.url");
USERNAME =ResourceBundle.getBundle("jdbc").getString("jdbc.username");
PASSWORD =ResourceBundle.getBundle("jdbc").getString("jdbc.password");
}
/**
* 注册驱动的方法:
*/
public static void loadDriver() {
try {
Class.forName(DRIVERCLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获得连接的方法
*/
public static Connection getConnection() {
Connection conn = null;
try {
// 加载驱动
loadDriver();
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 释放资源的代码
*/
public static void release(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs, Statement stmt, Connectionconn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
1.1.7 JavaEE的模式-DAO模式:
DAO:Data Access Object.数据访问对象.
* DAO:DAO中封装对数据源的单个操作,需要对外提供一组接口供业务层访问.访问DAO的时候传递一个Java对象.
* 使用DAO的模式完成CRUD的操作.
1.1.8 DAO的模式完成登录的案例:
步骤一:创建一个web项目,引入相应的jar包.
* mysql的驱动.
* jstl的包.
* beanutils的包.
步骤二:设计登录页面:
<h1>登录页面</h1>
<form action="" method="post">
<table border="1"width="400">
<tr>
<td>用户名</td>
<td><input type="text"name="username"/></td>
</tr>
<tr>
<td>密码</td>
<td><inputtype="password" name="password"/></td>
</tr>
<tr>
<tdcolspan="2"><input type="submit" value="登录"/></td>
</tr>
</table>
</form>
步骤三:创建包结构:
* cn.itcast.web.servlet
* LoginServlet
* cn.itcast.service
* UserService
* cn.itcast.dao
* UserDao
* UserDaoImpl
* cn.itcast.domain
* User
* cn.itcast.utils
* JDBCUtils
步骤四:编写LoginServlet:
* 接收参数
* 封装数据
* 调用业务层类
* 页面跳转.
步骤五:编写UserService:
* 调用DAO.
步骤六:编写UserDao.
* 使用JDBC的操作完成对数据库查询.
步骤七:设计登录成功页面:
1.1.9 SQL注入的漏洞:
SQL注入的漏洞在早期互联网中是普遍存在.
* 已知用户名的情况下.对这个用户名的用户进行攻击!!!
* 1.攻击方式一:
* 在用户名地方输入:aaa' or '1=1
* 密码随意.
* 2.攻击方式二:
* 在用户名地方输入:aaa' --
* 密码随意.
* 产生原因:因为在用户名地方输入SQL的关键字.(SQL注入)
* DAO中SQL语句:
* select * from user where username = '' andpassword = '';
* 产生原因一:
* select * from user where username = 'aaa' or '1=1' and password = 'xxxx';
* 产生原因二:
* select * from user where username = 'aaa' -- 'and password = 'xxxx';
* 解决SQL注入的漏洞:
* 1.使用JS在文本框进行校验.校验不可以输入 -,or , ' 特殊的字符都不可以输入.
* 不可行:JS的校验只是为了提升用户的体验.不能真正进行校验.(JS的代码可以被绕行.)
* 在地址上上直接输入访问路径.loginServlet?username=aaa'or '1=1&password=xxxxxx
* 2.SQL注入漏洞真正的解决方案:PreparedStatement.对SQL进行预编译.参数的地方都可以使用 ?作为占位符.
* select * from user where username = ? andpassword = ?;
* 再次输入 aaa' or '1=1 拿到参数之后. or 一些特殊的字符不当成SQL的关键字。只是当成普通的字符串进行解析.
* 提前进行编译.编译后的SQL的格式就已经固定了.
* 使用PreparedStatement改写了登录案例解决了SQL注入的漏洞!!!
1.1.10JDBC完成大文件的读写:(了解)
MYSQL数据库提供了两个数据类型:BLOB,TEXT用于存放文件.将文件存入到数据库.(数据库中存放的是上传文件路径.)
* Oracle:BLOB,CLOB.
文本文件的读写:
步骤一:创建一个表:
create table mytext(
id int primary key auto_increment,
data longtext
);
步骤二:读写文本文件:
@Test
/**
* 将dulala.txt存入到数据库中.
*/
public void demo1(){
Connection conn = null;
PreparedStatement stmt =null;
try{
// 获得连接:
conn =JDBCUtils.getConnection();
// 编写SQL:
String sql ="insert into mytext values (null,?)";
// 预编译SQL:
stmt =conn.prepareStatement(sql);
// 设置参数:
File file = newFile("src/dulala.txt");
Reader reader = newFileReader(file);
stmt.setCharacterStream(1,reader, (int)file.length());
// 执行SQL;
stmt.executeUpdate();
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(stmt,conn);
}
}
@Test
/**
* 将数据库中存放的dulala.txt获得到.
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt =null;
ResultSet rs = null;
try{
conn =JDBCUtils.getConnection();
// 编写SQL:
String sql ="select * from mytext where id = ?";
// 预编译SQL:
stmt =conn.prepareStatement(sql);
// 设置参数:
stmt.setInt(1, 1);
// 执行SQL:
rs = stmt.executeQuery();
// 获得结果集的数据
if(rs.next()){
Reader reader =rs.getCharacterStream("data");
Writer writer =new FileWriter("src/dulala_bak.txt");
// 两个流对接:
int len = 0;
while((len =reader.read())!=-1){
writer.write(len);
}
reader.close();
writer.close();
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
步骤三:修改Mysql的包大小.
* 在mysql的配置文件中:
[mysqld]下面添加:
max_allowed_packet=64M
二进制文件的读写:
步骤一:创建表
create table myblob(
id int primary key auto_increment,
data longblob
);
步骤二:读写大二级制文件:
@Test
/**
* 将love china.mp3文件存入到数据库.
*/
public void demo1(){
Connection conn = null;
PreparedStatement stmt =null;
try{
// 获得连接:
conn =JDBCUtils.getConnection();
// 编写SQL语句:
String sql ="insert into myblob values (null,?)";
// 预编译SQL:
stmt =conn.prepareStatement(sql);
// 设置参数:
File file = newFile("src/love china.mp3");
InputStream is = newFileInputStream(file);
stmt.setBinaryStream(1,is, (int)file.length());
// 执行SQL;
stmt.executeUpdate();
}catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.release(stmt,conn);
}
}
@Test
/**
* 将love china.mp3从数据库中读出来
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt =null;
ResultSet rs = null;
try{
// 获得连接:
conn =JDBCUtils.getConnection();
// 编写SQL:
String sql ="select * from myblob where id = ?";
// 预编译SQL:
stmt =conn.prepareStatement(sql);
// 设置参数:
stmt.setInt(1, 1);
// 执行SQL:
rs =stmt.executeQuery();
// 获得结果集数据:
if(rs.next()){
InputStream is =rs.getBinaryStream("data");
OutputStream os =new FileOutputStream("src/love china_bak.mp3");
int len = 0;
byte[] b = newbyte[1024];
while((len = is.read(b))!=-1){
os.write(b, 0,len);
}
is.close();
os.close();
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
TEXT:
* TINYTEXT(255),TEXT(64K),MEDIUMTEXT(16M),LONGTEXT(4G)
BLOB:
* TINYBLOB(255),BLOB(64K),MEDIUMBLOB(16M),LONGBLOB(4G)
1.1.11JDBC批处理:
批处理:一批SQL一起执行.
* 企业中使用批处理进行批量的插入,修改的操作.
批处理的案例:
* 写多条SQL一起执行.
使用批处理批量向数据库插入10000条记录.
* MYSQL插入10000条记录 大概花费3-4分钟.
* 使用?rewriteBatchedStatements=true参数提升批处理运行的效率.