概述
1. 数据的持久化
-
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
-
持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
2. Java中的数据存储技术
-
在Java中,数据库存取技术可分为如下几类:
-
JDBC直接访问数据库
-
JDO (Java Data Object )技术
-
第三方O/R工具,如Hibernate, Mybatis 等
-
-
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。
3. JDBC介绍
- JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这些类库可以以一种标准的方法、方便地访问数据库资源。
- JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
- JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
- 如果没有JDBC,那么Java程序访问数据库时是这样的:
有了JDBC,Java程序访问数据库时是这样的:
- 总结如下:
4. JDBC体系结构
- JDBC接口(API)包括两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。 ————面向接口编程
5. JDBC程序编写步骤
补充:ODBC(Open Database Connectivity,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。
第一个JDBC程序
1. 建立数据库
CREATE DATABASE jdbc CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbc;
CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')
2. 创建项目,这里使用Maven构建
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.peng</groupId>
<artifactId>jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--dbcp连接池-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
3. 测试代码
@SuppressWarnings("all")
public class jdbc {
@Test
public void jdbcdemo () throws ClassNotFoundException,SQLException{
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.用户信息和url
String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&&useSSL=false";
String name="root";
String password="peng";
//3.连接成功,返回数据库对象,connection代表数据库
Connection connection = DriverManager.getConnection(url, name, password);
//4.执行SQL的对象statement
Statement statement = connection.createStatement();
//5.执行SQL的对象,去执行SQL 可能存在结果,查看返回结果
String sql = "select * from users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println("name=" + resultSet.getObject("NAME"));
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
步骤总结:
加载驱动——连接数据库(DriverManager)——获取执行SQL的对象(Statement)——获得返回结果集——释放连接
Statement对象和PreparedStatement对象区别
Statement:
1. 创建db.properties存储配置信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&&useSSL=false
username=root
password=peng
2. 写工具类读取信
package com.peng.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtlis {
private static String driver=null;
private static String url=null;
private static String username=null;
private static String password=null;
static{
try {
InputStream in = JdbcUtlis.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver=properties.getProperty("driver");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
System.out.println(driver);
//驱动只需要加载一次
//Class.forName(driver);
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
3. 测试删除
@Test
public void TestDelete(){
Connection conn = null;
Statement st =null;
ResultSet rs=null;
try {
conn = JdbcUtlis.getConnection();
st = conn.createStatement();
String sql = "delete from users where id=3";
int delete = st.executeUpdate(sql);
if (delete>0){
System.out.println("delete success");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtlis.release(conn,st,rs);
}
}
4. 测试插入
@Test
public void TestInsert(){
Connection conn = null;
Statement st =null;
ResultSet rs=null;
try {
conn = JdbcUtlis.getConnection();
st = conn.createStatement();
String sql="insert into users(id,name,password,email,birthday)"+
"value(100,'刘备','123','123','1995-02-03')";
int insert = st.executeUpdate(sql);
if(insert>0)
System.out.println("insert success");
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtlis.release(conn,st,rs);
}
}
5. 测试更新
@Test
public void TestUpdate(){
Connection conn = null;
Statement st =null;
ResultSet rs=null;
try {
conn = JdbcUtlis.getConnection();
st = conn.createStatement();
String sql="UPDATE users SET id=3,`NAME`='彪彪',`PASSWORD`='123456',email='123456789@qq.com',birthday='1997-05-02' WHERE id = 100";
int update=st.executeUpdate(sql);
if(update>0)
System.out.println("update success");
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtlis.release(conn,st,rs);
}
}
6. 测试查询
@Test
public void TestSelect(){
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn= JdbcUtlis.getConnection();
st = conn.createStatement();
String sql="SELECT * FROM users";
//返回结果集
rs=st.executeQuery(sql);
while(rs.next()){
System.out.print(rs.getInt("id") + " ");
System.out.print(rs.getString("NAME") + " ");
System.out.print(rs.getString("PASSWORD") + " ");
System.out.print(rs.getString("email") + " ");
System.out.println(rs.getDate("birthday"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtlis.release(conn,st,rs);
}
}
PreparedStatement:
只测试查询,增删改方法都和上面的差不多,只是改了try()里面的一点代码
@Test
public void TestprepareStatementSelect(){
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn= JdbcUtlis.getConnection();
String sql="select * from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1,1);
rs = st.executeQuery();
while(rs.next()){
System.out.print(rs.getInt("id") + " ");
System.out.print(rs.getString("NAME") + " ");
System.out.print(rs.getString("PASSWORD") + " ");
System.out.print(rs.getString("email") + " ");
System.out.println(rs.getDate("birthday"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtlis.release(conn,st,rs);
}
}
Statement 和 PreparedStatement之间的关系和区别:
关系:PreparedStatement继承自Statement,都是接口
区别:PreparedStatement可以使用占位符,是预编译的,批处理比Statement效率高
详解:
1. PreparedStatement:表示预编译的 SQL 语句的对象
2. 接口:public interface PreparedStatement extends Statement之间的继承关系
3. SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句
4. Statement不能防止SQL注入,而PreparedStatement可以防止SQL注入。PreparedStatement本质是把传递进来的参数当做字符。 假设其中存在转义字符,如’ - 会被直接转义。
注:用于设置 IN 参数值的设置方法(setShort、setString 等等)必须指定与输入参数的已定义 SQL 类型兼容的类型。例如,如果 IN 参数具有 SQL 类型 INTEGER,那么应该使用 setInt 方法,问号的位置也是应该注意的,因为第一个问好的位置为1,第二个问号的位置为2.以此类推。
SQL注入攻击的原理连接:Sql注入基本原理_猿小雷的博客-CSDN博客_sql注入攻击的原理
JDBC事务
CREATE DATABASE IF NOT EXISTS `jdbc`;
CREATE TABLE `account`(
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '用户户id',
`name` VARCHAR(20) NOT NULL COMMENT '用户名字',
`money` FLOAT NOT NULL COMMENT '用户金钱',
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `account` (`name`,`money`) VALUES ('刘备',1000),('曹操',1000);
@Test
public void TestTransaction(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtlis.getConnection();
//关闭自动提交功能,开始事务
conn.setAutoCommit(false);
String sql1 = "UPDATE account set monet=money-100 WHERE name='刘备'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
//会导致操作失败,事务回滚。
int x=1/0;
String sql2 = "UPDATE account set monet=money+100 WHERE name='曹操'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕提交事务
conn.commit();
System.out.println("Success");
} catch (SQLException throwables) {
//如果失败则回滚
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
} finally {
JdbcUtlis.release(conn,st,rs);
}
}
DBCP数据库连接池
数据库连接——执行完毕——释放十分消耗资源
池化技术:准备一些预先的资源,过来就连接,使用预先准备好的服务。
- 常用连接数10个
- 最小连接数10个
- 最大连接数15个(超过15个请求需要排队)
- 等待超时:等待时间超过一定值直接失败
编写连接池:实现DataSource接口,开源数据源实现:DBCP、C3P0、Druid。使用连接池,省去直接使用getconnection()即可(不用手动编写)。
1. 配置文件
报错试设置useSSL=false;
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=peng
#!-- 初始化连接 --
initialSize=10
#最大连接数量
maxActive=50
#!-- 最大空闲连接 --
maxIdle=20
#!-- 最小空闲连接 --
minIdle=5
#!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:user 与 password 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
2. 工具类
package com.peng.utils;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils_DBCP {
private static DataSource dataSource=null;
static {
try {
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp_config.properties");
Properties properties=new Properties();
properties.load(in);
//创建数据源 工厂模式->创建
dataSource= BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();//从数据源获取连接
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
3. 测试查询,增删改都差不多
@Test
public void TestDBCP(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils_DBCP.getConnection();
st = conn.createStatement();
String sql = "SELECT * FROM users";
//返回结果集
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.print(rs.getInt("id") + " ");
System.out.print(rs.getString("NAME") + " ");
System.out.print(rs.getString("PASSWORD") + " ");
System.out.print(rs.getString("email") + " ");
System.out.println(rs.getDate("birthday"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils_DBCP.release(conn, st, rs);
}
}
4. 项目结构图