一个思想
- ORM编程思想
一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性
(java类中的属性名需要和表中的字段名相同
或者在sql语句中给字段名取别名否则查询不出数据
)
-
PreparedStatement与Statement的区别PREparedStatement是Statement的一个子接口
-
使用PrepareStatement替换Statement,除了1.可以解决sql注入问题2.可以解决Statement的拼串问题
之外还可以操作Blob的数据,而Statement做不到,2可以实现更高效的批量操作Mysql的四种BLOB类型除了(在存储的最大信息量上不同外,其他是相同的) {
TinyBlob 最大255字节
Blob 最大65k
MediumBlob 最大16m
LongBlob 最大4G
} -
使用PreparedStatement不使用Statement的好处是编译sql语句时 Statement每使用一次需要创建一
次而PreparedStatement只需要创建编译一次完成之后在后续执行时插入等sql语句时只用填充站位符而不用重写创建sql了。并且PreparedStatement解决了Sql注入的问题。 -
-
-
使用PreparedStatement实现对数据库的增删改查的一些通用操作
-
-
创建三个变量
-
public Connection conn = new Connection(); public PreparedStatement ps = new PreparedStatement(); public ResultSet rs = new ResultSet();
-
-
//创立链接
String url = "jdbc:mysql://localhost:3306/数据库的名称?characterEncoding=utf8";
String user = "数据库链接的用户名";
String password = "密码";
//建立驱动
Class.forName("com.mysql.jdbc.Driver");//通过反射创建驱动
//获取链接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
-
查询操作
ps.execute():如果执行的是查询操作,返回结果集
如果是增删改操作,返回false。
//获取数据库中的数据返回给实现类去处理
public ResultSet selet(String sql,Object ...args){
//获取链接
conn = getConn();
try {
//预编译sql语句
ps = conn.prepareStatement(sql);
if(args!=null) {
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
}
//执行操作
rs = ps.executeQuery();//获取结果集
} catch (Exception e) {
e.printStackTrace();
}
return rs;//返回结果集
} -
增删改的方法
-
public int executeUpadate(String sql, Object... params) {
int ret = 0;conn = getConnection();
try {
ps = conn.prepareStatement(sql);if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
System.out.println(params[i]);
}
}// ps.executeUpdate();添加成功返回1;失败返回0。ret = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
closeAll(conn, ps, null);
}
return ret;}
-
-
批量插入数据到数据库中
-
//方式二。 // 1.addBatch(),executeBatch(),clearBatch(); // 2.Mysql服务器默认关闭批处理的,我们需要通过一个参数,让mysql开区批量处理的支持。 // ?rewriteBatchedStatements=true写在配置文件url后面 @Test public void Insert2() { long start = System.currentTimeMillis(); try { conn = getConn(); String sql = "INSERT into goods(`name`) VALUES(?);"; ps = conn.prepareStatement(sql); for (int i = 1; i <= 20000; i++) { ps.setObject(1,"name_"+i); //1.“攒”sql ps.addBatch(); if(i%500==0){ //执行Batch ps.executeBatch(); // 清空Batch ps.clearBatch(); } } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { CloseAll(conn,ps,rs); } long end = System.currentTimeMillis(); System.out.println(end-start);//17s }
-
方式二。设置不允许自动提交(使用
//(使用事务)设置不允许自动提交数据 conn.setAutoCommit(false);
conn.commit();//开启提交
-
//方式三。设置不允许自动提交数据 @Test public void Insert3() { long start = System.currentTimeMillis(); try { conn = getConn(); //(使用事务)设置不允许自动提交数据 conn.setAutoCommit(false); String sql = "INSERT into goods(`name`) VALUES(?);"; ps = conn.prepareStatement(sql); for (int i = 1; i <= 20000; i++) { ps.setObject(1,"name_"+i); //1.“攒”sql ps.addBatch(); if(i%500==0){ //执行Batch ps.executeBatch(); // 清空Batch ps.clearBatch(); } } conn.commit(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { CloseAll(conn,ps,rs); } long end = System.currentTimeMillis(); System.out.println(end-start);//插入20000条数据2s }
-
-
-
数据库事务
数据库事务的介绍:
四大属性:原子性,一致性,隔离性,持久性。
1.事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
2、事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,
都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),
那这些数据就永久的保存下来;要么数据库管理系统将放弃所作的所有操作。使整个事务回滚(rollback)到最初的状态。
3.为确保数据库中的数据一致性,数据的操纵应当是离散的成组的逻辑单元;当它全部完成时
,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,
所有的操作都退回到初始状态。
4.数据一旦提交就不可回滚。
.那些操作会导致数据的提交
>DDL操作一旦执行,都会自动提交(set autocommit = flase对DDL操作失效)
>DML默认情况下,一旦执行,会自动提交(我们可以通过set autocommit = flase
的方法取消DML操作的自动提交。)
>默认在关闭链接时,会自动提交数据。
JDBC处理事务对数据库进行操作。
// 1.一个事务一条连接 2.取消自动提交 3.提交数据 4.出现异常,回滚数据
@Test
public void tex1() {
Connection conn1 = null;
try {
conn1 = JdbcUitu.getConn1();
System.out.println(conn1.getAutoCommit());
//取消数据的自动提交。
conn1.setAutoCommit(false);
String sql1="UPDATE user_table set balance = balance-100 WHERE user =?";
CRU(conn1,sql1,"AA");
// 模拟网络异常
System.out.println(10/0);
String sql2="UPDATE user_table set balance = balance+100 WHERE user =?";
CRU(conn1,sql2,"BB");
System.out.println("转账成功");
//提交数据
conn1.commit();
} catch (Exception throwables) {
throwables.printStackTrace();
//回滚数据
try {
conn1.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
if(conn1!=null){
try {
conn1.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
//增删改查的升级操作,考虑数据库事务。
//传入连接 conn
public int CRU( Connection conn ,String sql,Object ...args){
;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获取数据库链接
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充站位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//执行操作
// ps.execute();:
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
JdbcUitu.CloseAll(conn,ps,rs);
}
return 0;
}
2.查询升级,考虑上事务的通用方法
//查询操作,使用上事务的查询操作 用于返回数据表的多条记录
public <T> List<T> getForlist(Connection conn,Class<T> clazz, String sql, Object...args){
PreparedStatement ps=null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
while(rs.next()){
T t = (T) clazz.newInstance();
//给t对象指定属性的赋值
for (int i = 0; i < columnCount; i++) {
//获取每个列中的值
Object object = rs.getObject(i+1);
//获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过列名获取类中的属性。
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,object);
}
list.add(t);
}
return list;
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
try {
if(conn!=null){
conn.close();
}
if(ps != null){
ps.close();
}
if(rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
5.数据库连接池、
5.1JDBC连接池的必要性
- 在进行web开发时,传统的都是以下步骤
- 在主程序中建立数据库连接
- 进行sql操作
- 断开数据库连接
- 这种开发模式,存在的问题
- 普通的JDBC数据库连接使用DriverManager来获取,每次获取都要将Connection加载如内存当中,在验证用户名和密码。需要连接时就要向数据库请求一个,然后再关闭。如果连接多了就会浪费大量时间和资源,数据库的连接没有得到很好的复用。
- 对于每次数据库连接,使用过后都要断开,如果程序出现异常,不能关闭,将会导致数据库内存泄露,最终将导致重启数据库。
- 这种开发不能控制被创建连接的数据,如果创建过多,也会导致内存泄露,服务器崩溃。
5.2数据库连接池
- 数据库连接池的基本思想;
- 就是为数据库连接建立一个“缓冲池”,预先在缓冲池中放入一定数量的连接,需要建立数据库连接时,只需要从”池“中取出一个,使用完后在放回去。
- 数据库连接池负责分配,允许应用程序重复使用一个连接,而不是重新创建一个。
- 数据库连接池在初始化时将创建一定数量的数据库连接放入数据库连接池中,创建数据库连接的最小数量由最小数据库连接决定的,无论这些连接是否使用,连接池中都保存有这么多的数量,最大连接数量限定了这个连接池最大存有的连接数,如果程序请求的连接数量,大于了连接池中的最大连接数,这是请求将被加入等待队列中。
- 优点:
- 资源的重复利用
- 更快的反应速度
- 新的资源分配手段
- 统一的连接管理,避免了数据库连接的泄露。
- 多中开源的JDBC连接池
- JDBC数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由服务器(Weblogic,Tomcat)提供。
- DBCP是Apache提供的数据库连接池,也是Tomcat自带的,速度相对于此c3p0较快,
- C3P0,由一个开源组织提供的,速度较慢,稳定性还可以
- Proxool,有监控数据库连接池的功能,稳定性交C3P0差点
- BoneCP由一个开源组织提供的,速度快
- Druid:由阿里提供,集DBCP,C3P0,Proxool,BoneCP优点于一身
- JDBC数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由服务器(Weblogic,Tomcat)提供。
- Druid数据库连接池的使用
- Druid的相关配置信息
-
-
需要先创建一个properties配置文档,在其中写入如下信息
url=jdbc:mysql:///test username=root password=123456 driverClassName=com.mysql.jdbc.Driver //数据库默认连接数量 initialSize=10 //最大连接数量 maxActive=10
- 然后在util下写入连接代码
-
//使用Druid数据库连接池获取连接。 //创建一个Druid的数据库连接池 DataSource所有数据库连接池的接口。 private static DataSource sour; static { try { //读取配置文件信息 Properties prop = new Properties(); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("Druid.Properties"); prop.load(is); //获取Druid数据库连接池 sour = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection3() throws Exception { Connection connection = sour.getConnection(); return connection; }
6.Dbuitljar包的使用
- Dbuitl时Apache组织提供的一个JDBC工具类库,对JDBC的简单封装。封装了针对数据库的增删改查操作。
- 测试
- 插入一条数据。
@Test
public void testInset() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JdbcUitu.getConnection3();
String sql = "insert into user(name,password,address,phone)VALUES(?,?,?,?);";
int update = runner.update(conn, sql, "黎小冬", "123456", "郴州", "1888888888");
sql = "DELETE from user WHERE id =?";
for (int i = 7; i < 10; i++) {
int update1 = runner.update(conn, sql,++i);
if(update1!=0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
if (update!=0){
System.out.println("插入成功");
}else {
System.out.println("插入失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUitu.CloseAll(conn,null,null);
}
}
- 查询数据
-
查询测试: BeanHandler是ResultSetHandler的一个实现类,用于封装表中的一条记录。返回一个具体的对象 查询时需要用到实体类中的get、set方法 所以在写实体类时需要创建get和set方法 */ @Test public void testSelect() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JdbcUitu.getConnection3(); String sql = "select id,name,password,address,phone from user where id = ?"; BeanHandler<User> beanHandler = new BeanHandler<User>(User.class); User query = runner.query(conn, sql, beanHandler, 11); System.out.println(query); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUitu.CloseAll(conn,null,null); } }
-