基于MySQL的JDBC的知识总结

内容

  • jdbc概述
  • 数据库连接
  • 使用PreparedStatement实现crud
  • Blob操作
  • 批量插入
  • 事物
  • 连接池
  • apache的DBUtils

javaweb最简版的架构(c/s b/s
在这里插入图片描述

B/S技术体系
html:浏览器骨架 超文本标记语言
css:炫酷的页面 静态显示
JavaScript:行为
jQuery:是一个库,封装了js;也是一个前端框架(其它前端框架:vue易、react难、ajs国内不怎么用了)
tomcat:服务器
xml:可扩展标记语言 写配置文件
servlet:tomcat组件——获取请求、处理请求、响应请求
jsp:响应请求的页面显示 动态显示 本质也是servlet
EL:替代jsp表达式
JSTL:替代jsp脚本中断
cookie:识别是否来自同一个浏览器 客户端
session:识别是否来自同一个浏览器 服务器端
filter:tomcat组件
listener:tomcat组件
ajax:实现异步请求,部分改变
json:数据交换格式,传输数据用的
在这里插入图片描述

1.jdbc概述

1.1 数据持久化:断电之后数据不会消失

1.2 Java中的数据存储技术:

  • jdbc Java数据库连接 直接访问数据库 独立于特定的数据库
  • jdo Java data object
  • 第三方O/R工具 一些框架如hibernate和mybatis

1.3 JDBC提供统一接口,实现依赖具体的数据库,由数据库厂商提高实现类
在这里插入图片描述

1.4 JDBC编程步骤

  • 导入java.sql包
  • jdbc-odbc桥接式/纯jdbc驱动
  • 加载并注册驱动程序(java.sql.*)
  • 创建Connection对象
  • 创建Statement对象
  • 执行sql
  • 使用ResultSet对象
  • 关闭资源:ResultSet 对象、Statement对象、Connection对象

2.数据库连接
2.1 驱动Driver获得连接对象

方式一:

 Driver driver=new com.mysql.jdbc.Driver();//实现类of java.sql.Driver
 String url="jdbc:mysql://localhost:3306/test"//jdbc url:协议:子协议(标识数据库驱动程序)://子名称(标识数据库=主机名:端口号/数据库名称)
 Properties prop=new Properties();
 prop.setProperty("user""root");
 prop.setProperty("password","123");
 Connection connection=driver.connect(url,prop);

方式二:

 //不希望出现第三方com.sql.jdbc.Driver,反射来解决
 Class clazz=Class.forName("com.mysql.jdbc.Driver");
 Driver driver=(Driver)clazz.newInstance();
 ...//与方式一一样获取connection

方式三:

String url="jdbc:mysql://localhost:3306/test";
String user="root";
String password="123";

Class clazz=Class.forName("com.mysql.jdbc.Driver");
Driver driver=(Driver)clazz.newInstance();
DriverManager.register(driver);
Connection conn=DriverManager.getConnection(url,user,password);

方式四:

String url="jdbc:mysql://localhost:3306/test";
String user="root";
String password="123";
//省略掉的方式三中的部分是因为mysql的Driver中已在静态代码块中以实现
Class.forName("com.mysql.jdbc.Driver");
Connection conn=DriverManager.getConnection(url,user,password);

方式五(推荐):
jdbc.properties
diver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=123

//读取配置文件的方式jdbc.properties
InputStream resource=当前类.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties prop=new Properties();
prop.load(resource);
String driver=prop.getProperty("driver");
String url=prop.getProperty("url");
String user=prop.getProperty("user");
String password=prop.getProperty("password");
Class.forName(driver);
Connection conn=DriverManager.getConnection(url,user,password);

方式五的优势:换数据库只需要改配置文件即可,不需要改代码,解耦数据与代码

2.2 Statement
通过conn.createStatement()来获取
该对象调用 int executeUpdate(String sql)/ResultSet executeQuery(String sql)来执行sql语句
其弊端:

  • 问题一:存在拼接,操作繁琐 eg “select name from user where id=”+id;
  • 问题二:存在SQL注入(不充分检查数据而造成非法sql)

PreparedStatement可以避免Statement弊端,此外其具有更高的效率,因为预编译所以对于只是参数不同的同一条sql只编译一次,而Statement会编译n次
在这里插入图片描述

3.使用PreparedStatement实现crud
PreparedStatement对象通过conn.prepareStatement()来获取

//准备sql
try{
Connection conn=JDBCUtils.getConnection();//使用工具类,工具类具体内容在下面

//增
String sql="insert into user(id,name,birth) values(?,?,?)";//?为占位符,占位符从1开始编号
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1,"202001");
ps.setString(2,"煤气罐");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date=sdf.parse("2008-02-02");
ps.setDate(3,date);
//执行sql
ps.execute(sql);

//改
String sql="update user set name=? where id=?";
PreparedStatement ps=conn.preparsStatment(sql);
ps.setObject(1,"tom");
ps.setInt(2,11);
ps.execute();

//通用增删改
String sql="";
JDBCUtils.execute(conn,sql,参数们);

//查
String sql="select * from user";
PreparedStatment ps=conn.prepareStatement(sql);
ResultSet set=ps.executeQuery();
while(set.next()){
	User u=new User();//表对应的pojo
	u.setId(set.getString(1));
	u.setString(set.getString(2));
	u.setBirth(set.getDate(3));
	System.out.println(u.toString());
}

//通用查
String sql="";
JDBCUtils.executeQuery(conn,sql,参数们);

}catch(Exception e){	
	e.printStackTrace();
}finally{
	//关闭资源	
	BCUtils.close(conn,ps);
}

ORM思想 object relational mapping

  • 一个表对应一个类
  • 表中字段对应类的属性
    在这里插入图片描述
  • 一条记录对应一个类的实例对象

JDBCUtils
!!这里举例的查询通用方法只是显示,可以优化成返回结果集,然后再编写一个处理结果集的工具类

public class JDBCUtils{
private Connection conn=null;
static{
	InputStream resource=ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
	Properties prop=new Properties();
	prop.load(resource);
	String driver=prop.getProperty("driver");
	String url=prop.getProperty("url");
	String user=prop.getProperty("user");
	String password=prop.getProperty("password");
  	Class.forName(driver);
  	conn=DriverManager.getConnection(url,user,password);
  }
	//获取连接
	public static Connection getConnection() throws Exception{		
		return conn;
	}

	//关闭资源
	public static void close(Connection conn, Statement s){//PreparedStatement继承于Statement
  		try{if(s!=null)
   			s.close();
  		}catch(...){...}
  		try{if(conn!=null)
   			conn.close();
  		}catch(...){...}
	}
	public static void close(Connection conn,Statement s,ResultSet result){
		try{}catch...//同上,多一个result的关闭
	}

	//通用增删改
	public static void execute(Connection conn,String sql,Object ...args) throws Exception{
		PreparedStatement ps=conn.prepareStatement(sql);
		for(int i=0;i<args.length;i++){
			ps.setObject(i+1,args[i]);
		}
		ps.execute();
	}
	
	//通用查,这里只针对某个表的所有查询,不是针对不同的表
	public static void executeQuery(Connection conn,String sql,Object ...args){
		PreparedStatement ps=conn.prepareStatement(sql);
		for(int i=0;i<args.length;i++)
			ps.setProperty(i+1,args[i]);
		ResultSet result=ps.executeQuery();
		//获取结果集的元数据
		ResultSetMetaData rs=result.getMetaData();
		int column=rs.getColumnCount();
		while(result.next()){
			User u=new User();
			for(int i=0;i<column;i++){
				Object o=result.getObject(i+1);
				
				//获取列名
				String cn=rs.getColumnName(i+1);
				//赋给属性
				Field f=User.class.getDeclaredField(cn);//当属性名和表字段名不一致时not OK
				String cn=rs.getColumnLabel(i+1);//如果没其起别名就返回列名,以后不使用getColumnName()
				Field f=User.class.getDeclaredField(cn)//要在sql中给字段起别名,即同属性名,才可使这个语句正确
				
				f.setAccessible(true);//以防属性为私有
				f.set(u,f);
			}
			System.out.println(u.toString());
		}
	}

	//通用查,针对不同表
       public static void executeQuery(Connection conn,String sql,Class<T> clazz,Object ...args){
		...//相同代码省略
		while(result.next()){
			T t=clazz.newInstance();
			...
		}
	}
}

JDBC API小结

  • 两种思想:面向接口编程,ORM思想
  • 两种技术:获取元数据集,反射

4.Blob(tinyblob 255 blob 65k mediumblob 16M longblob 4G)二进制大型数据类型

FileInputStream in=new FileInputStream(new File("1.jpg"));
ps.setBlob(5,in);
//别忘了关闭流

//获取结果集中blob
Blob photo=result.getBlob(5);
Inputstream binaryStream=photo.getBinaryStream();
FileOutputStream out=new FileOutputStream("2.jpg");
byte[] b=new byte[1024];
int len;
while((len=binaryStream.read(b))!=-1){	
	out.write(b,0,len);
}

注意事项:

  • 依据数据大小选合适的blob类型
  • 文件过大会降低数据库性能
  • Statement无法操作blob数据
  • 如果不是因为类型原因装不下文件,就去改my.ini:max_allowed_packet=16M。!!修改了my.ini要重启数据库才生效

5.批量操作
!!使用PreparedStatement实现高效的批量插入

PreparedStatementStatement
预编译,可复用编译的语句不能预编译,导致相同执行语句因为数据不一样要重新编译

!!编译意味着要语法检查、语义检查,语句翻译所以费时,预编译需要缓存编译好的内容,以以空间换时间

//这里记得要try-catch一下,有资源关闭的地方都需要,为了关注批量操作本身这里省略没写
//注意变量定义要放在try-catch语句之外
Connection conn=JDBCUtils。getConnection();
conn=setAutoCommit(false);//不允许自动提交,别执行就提交,让它最后提交,优化速度
String sql="insert into user(name) values(?)";
PreparedStatement ps=conn.prepareStatement(sql);
for(int i=0;i<=20000;i++){
	ps.setObject(1,"user_"+i);
	//攒sql
	ps.addBatch();//添加批处理的参数
	if(i>0&&i%500=0){
		//执行batch
		ps.executeBatch();
		//清空batch
		ps.clearBatch();
	}
}
conn.commit();
JDBCUtils.close(conn,ps);

!!mysql正常情况下是关闭批处理的,需要在配置文件中开启:写在url后面rewriteBatchedStatements=true
jdbc.properties
diver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
user=root
password=123

6.事物
6.1 概念:一组逻辑操作单元(DML操作,简单来说就是增删改),使数据从一种状态变为另一种一致的状态
6.2 原则:要么全做成功,要么全部没做,所以有失败则回滚

!!数据一旦提交就不可回滚

6.3 会自动提交的操作

  • DDL,且set autocommit=false对其设置无效
  • DML,可以通过set autocommit=false来阻止其自动提交
  • 默认再关闭数据库连接时会自动提交
try{
	conn.setAutoCommit(false);
	事物
	conn.commit();
}
catch(Exception e){
	conn.rollback();}
finally{
	conn.setAutoCommit(true)//以防别人要用自动提交,针对数据库连接池
	关闭资源
}

6.4 ACID属性

  • 原子性automicity:事物是不可分割的工作单元,要么全执行,要么全不执行
  • 一致性consistency:事物从一个一致状态转换为另一个一致性状态
  • 隔离性isolation:并发事物互不干扰
  • 持久性durability:事物一旦提交,就是永久性改变,接下来发生的任何事情对它都没有影响

6.5 数据库并发三个问题

  • 脏读:两事物,T1改了数据但未提交,T2读到了T1改了的数据
  • 不可重复读:T2读数据,T1改数据并提交,T2又又读了一遍,发现两次结果不一样
  • 幻读:T2读数据,T1增加了几行数据并提交,T2又又读了一遍,发现多了几行

6.5 隔离级别

  • read uncommitted:三问题都没解决
  • read commited:解决了脏读 Oracle默认(Oracle支支持2和4)
  • repeatable read:解决了不可重复读和脏读 MySQL默认 (mysql都支持)
  • serializable:三个问题都解决了
    !!一致性越好性能越差,即并发性越差

6.7 MySQL中的命令
创建用户:create user 用户名 identified by ‘密码’;
赋予权限:grant select,insert,update,delete on 库名.* to 用户名@主机名 identified by ‘密码’;
查看隔离级别:select @@tx_isolation;
设置隔离级别:set global transaction isolation level 隔离级别;

//获取隔离级别
conn.getTransactionIsolation();
//设置隔离级别
conn.setTransctionIsolation(Connection.TRANSACTION_READ_COMMITED);

7.数据库连接池
7.1 普通方式获取连接及关闭连接的弊端

  • 连接的获取需要放到内存中占用内存资源,并需要进行用户名和密码的验证,费时费力;如果执行完操作就关闭连接,会造成资源浪费
  • 如果未关闭成功会造成内存泄漏,甚至需要重启数据库(java中内存泄漏指不能被垃圾回收)
  • 连接数量不可控制,造成数据库崩溃

7.2 数据库连接池可以解决这些问题

  • 预先放在池中一些连接,需要时从里面拿,用完放回
  • 依需增加连接和减少连接,对连接进行管理

7.3 JDBC开源的数据库连接池(DataSource的实现类)

  • DBCP:apache提供,tomcat自带,速度比C3P0快,但有一些自身bug BasicDataSource,它用工厂模式创建
  • C3P0:速度较慢,但稳定 ComboPooledDataSource
  • Druid:阿里提供,既稳定又快 DruidDataSource
  • Proxool:比C3P0性能差一点
  • BonCP:速度快
    !!使用哪个要导入其jar包,参考文档使用

7.4 数据源(DataSource 接口):连接池+连接池管理 使用DataSource代替DriverManager获取连接高效快速

以C3P0为例

数据源对象ds
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("...");
ds.setUser("..");
ds.setPassword("...");
ds.setInitialPoolSize(10);
等等设置
DataSource.destroy(ds);//销毁连接池

!!也可通过配置文件xml操作 cpds=new ComboPooledDataSource(“配置文件名”);
在JDBCUtils中使用连接池获取连接

private static ComboPooledDataSource cpds=new ComboPooledDataSource();//池子有一个就可以了
public static Connection getConnection() throws SQLException{
	return cpds.getConnection();
}

8.Apache—DBUtils
替代我们自己编写的JDBCUtils
API:

  • org.apache.commons.dbutils.QueryRunner:增删改查操作,这是个类
  • org.apache.commons.dbutils.ResultSetHandler:结果集处理器,这是个接口
  • org.apache.commons.dbutils.DbUtils:关闭资源的操作,这是个类
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值