一:JDBC操作事务
1.1:事务异常回滚
事务控制
连接.setAutoCommit(false); 开启事务
连接.rollback();
连接.commit();
connection.setAutoCommit(false);
connection.setAutoCommit(true);
@Test
public void test01() throws SQLException, IOException, ClassNotFoundException {
Connection connection = null;
PreparedStatement pst = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);
String sql = "update account set balance = balance + ? where id = ?";
pst = connection.prepareStatement(sql);
//填充数据
pst.setObject(1, -1000);
pst.setObject(2, 1);
//执行操作
int i = pst.executeUpdate();
System.out.println("i = " + i);
// System.out.println("操作成功");
int i1 = Integer.valueOf("ABCD");
pst.setObject(1,1000);
pst.setObject(2,2);
int i2 = pst.executeUpdate();
System.out.println("i2 = " + i2);
} catch (Exception e) {
// e.printStackTrace();
System.out.println("出现异常回滚");
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
try {
connection.setAutoCommit(true);
JDBCUtils.closeResource(null,connection,pst);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1.2:数据库存储图片
blob 64kb (blob数据类型可以存64kb)
longblob 4gb 可以存储 字节数据
但是配置文件中my.ini 默认 max_allowed_packet=4M 单次上传的数据最大是4兆
一般是不使用mysql 直接存储图片(一张表的大小才n个kb,有的照片会很大影响查询速率)
在实际开发中 中单独的图片服务器 在mysql中只是存储 图片路径
注意:在插入图片的时候一定要查看图片大小,否则插不进去
@Test
public void test01() throws SQLException, IOException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into account values(?,?,?,?)";
PreparedStatement pst = connection.prepareStatement(sql);
FileInputStream fileInputStream = new FileInputStream("G:\\img\\03.jpeg");
/* byte[] bytes = new byte[304];
int read = fileInputStream.read(bytes);*/
pst.setObject(1,5);
pst.setObject(2,"MM3");
pst.setObject(3,3000);
pst.setObject(4,fileInputStream);
pst.executeUpdate();
JDBCUtils.closeResource(null,connection,pst);
}
二:数据库连接池
2.1:概念
连接池是connection对象的缓冲区,它里面会存放一些connection,当我们Java程序需要使用connection的时候,如果连接池中有则直接从连接池获取,不需要去新创建connection了。连接池让Java程序能够复用连接、管理连接
2.2:为什么要使用连接池
1.因为每次创建和销毁连接都会带来较大的系统开销
2.每次创建和销毁连接都要消耗大概0.05~1s的时间。
3.可以防止大量用户并发访问数据库服务器。
2.3:连接池的作用
1:资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)
2:更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3:统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。
举例:特别是在银行的App中,如果登录账户之后,一段时间没有进行任何操作,当再次回到这个页面的时候会,需要重新登录,这就是连接被收回了,防止数据泄露。
2.4:连接池的原理
1>连接池维护着两个容器空闲池和活动池
2>空闲池用于存放未使用的连接,活动池用于存放正在使用的连接,活动池中的连接使用完之后要归还回空闲池
3>当Java程序需要连接时,先判断空闲池中是否有连接,如果空闲池中有连接则取出一个连接放置到活动池供Java程序使用
4>Java程序需要连接时,如果空闲池中没有连接了,则先判断活动池的连接数是否已经达到了最大连接数,如果未达到最大连接数,则会新创建一个连接放置到活动池,供Java程序使用
5>如果空闲池中没有连接了,活动池中的连接也已经达到了最大连接数,则不能新创建连接了,那么此时会判断是否等待超时,如果没有等待超时则需要等待活动池中的连接归还回空闲池
6>如果等待超时了,则可以采取多种处理方式,例如:直接抛出超时异常,或者将活动池中使用最久的连接移除掉归还回空闲池以供Java程序使用
2.5:连接池的实现
DataSource接口:
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口(通常被称为数据源),所有的Java数据库连接池都需要实现该接口。该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现
2.6:Druid连接池的使用
(1)加入jar包
(2)代码步骤
第一步:创建druid连接池的配置文件druid.properties文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test username=root password=123456 initialSize=5 maxActive=10 maxWait=1000
2.6.1:DruidDataSourceFactory作用
DataSource是一个接口 其中只有一个抽象方法 用来返回一个连接
Connection getConnection(String username, String password) throws SQLException;
DruidDataSourceFactory用来创建Druid连接池对象
DruidDataSourceFactory中的方法createDataSource,会间接返回一个datasource对象
public static DataSource createDataSource(Properties properties) throws Exception { return createDataSource((Map)properties); } public static DataSource createDataSource(Map properties) throws Exception { DruidDataSource dataSource = new DruidDataSource(); config(dataSource, properties); return dataSource; }
@Test
public void test01() throws Exception {
//1. 创建一个Properties对象,让其去读取
Properties properties = new Properties();
properties.load(DataSourceTest.class.getClassLoader().getResourceAsStream("druid.properties"));
//2. 使用DruidDataSourceFactory创建Druid连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//第三步:使用连接池对象获取连接
Connection connection = dataSource.getConnection();
System.out.println("connection = " + connection);
}
2.6.2DruidDataSource
@Test
public void test02() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/myexer?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
DruidPooledConnection connection = dataSource.getConnection();
System.out.println("connection = " + connection);
}
2.6.3DruidDataSource 连接池实现复用效果
@Test
public void test02() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/myexer?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setMaxActive(10);
dataSource.setInitialSize(5);
dataSource.setMaxWait(1000);
for (int i = 0; i < 10; i++) {
DruidPooledConnection connection = dataSource.getConnection();
System.out.println("connection = " + connection +" " +i);
connection.close();
}
}
原因是:连接使用完之后 就立马放回到了连接池中,因此可以进行复用。
2.6.4DruidDataSource 的封装工具类
public class JDBCDruidUtils {
static DataSource dataSource;
static {
Properties properties = new Properties();
try {
properties.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取一个连接的时候 主要得到connection 此时 获取dataSource本身就是一个连接池
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
return connection;
}
public static void closeResources(ResultSet resultSet , Connection connection , Statement statement){
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
三:Apache的DBUtils
3.1:DBUtils的概述
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装
使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
QueryRunner类封装了SQL的执行,是线程安全的
(1)可以实现增、删、改、查、批处理
(2)考虑了事务处理需要共用Connection
(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
3.2:主要的类
QueryRunner() ,创建QueryRunner对象,用于执行SQL语句
QueryRunner的update(Connection conn, String sql, Object... params)方法,用于执行增删改的SQL语句
3.3:QueryRunner()的两个构造器 空参和参数是连接池
public QueryRunner() {
}
public QueryRunner(DataSource ds) {
super(ds);
}
注意:当传入连接池的时候,在进行执行sql的时候就不需要传入连接了,例如
test04。
public class DbUtilTest {
@Test
public void test05() throws SQLException {
//简化
int update = new QueryRunner(JDBCDruidUtils.getDataSource()).update("insert into users values(null,?,?,?)", "lbb", "123456", "李冰冰");
System.out.println("update = " + update);
}
@Test
public void test04() throws SQLException {
//1:创建一个带有连接池的QueryRunner
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2:创建sql
String sql = "insert into users values(null,?,?,?)";
//3:执行sql
int update = queryRunner.update(sql, "wbb", "123456", "王冰冰");
System.out.println("update = " + update);
}
@Test
public void test03() throws SQLException {
//1:创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//2:获取连接
Connection connection = JDBCDruidUtils.getConnection();
//3:创建sql
String sql = "update users set name = ? ,nickName = ? where id = ?";
int update = queryRunner.update(connection, sql, "fbb","范冰冰", 1);
System.out.println("update = " + update);
}
@Test
public void test02() throws SQLException {
//1:创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//2:创建连接
Connection connection = JDBCDruidUtils.getConnection();
//3:创建sql
String sql = "delete from users where id = ?";
int i = queryRunner.update(connection, sql, 2);
System.out.println("i = " + i);
}
@Test
public void test01() throws SQLException {
//1:创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//2:创建sql
String sql = "insert into users values(null,?,?,?)";
//获取一个连接
Connection connection = JDBCDruidUtils.getConnection();
//3:填充数据 并执行sql
int i = queryRunner.update(connection,sql, "lbb","123456","李冰冰");
System.out.println("i = " + i);
//关闭资源
JDBCDruidUtils.closeResources(null,connection,null);
}
}
四:DBUtils执行批处理
4.1:API介绍
public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException
: 支持批处理INSERT, UPDATE, or DELETE语句
public <T> T insertBatch(Connection conn,String sql,ResultSetHandler<T> rsh,Object[][] params)throws SQLException
:只支持INSERT语句
4.2:批处理顺序
1. 创建QueryRunner对象
2. 编写SQL语句
3. 设置二维数组参数:第一维表示我们需要处理多少条数据,第二维表示sql语句有多少个参数
4:填充数据
5:执行批处理 方法是 queryRunner.batch(sql,params);
4.2:代码实现
public class DBatchTest {
@Test
public void test03() throws SQLException {
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
String sql = "insert into users values(null,?,?,?)";
String[][] params = new String[5000][3];
for (int i = 0; i < params.length; i++) {
params[i][0] = "fbb" + i;
params[i][1] = "123456";
params[i][2] = "范冰冰" + i;
}
queryRunner.batch(sql, params);
}
@Test
public void test02() throws SQLException {
//1:创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2: 创建sql
String sql = "insert into users values(null,?,?,?)";
//3:创建数组
Object[][] params = new Object[5000][3];
for (int i = 0; i < params.length; i++) {
/* params[i][0] = "fbb" + i;
params[i][1] = "123456";
params[i][2] = "范冰冰" + i;
*/
}
queryRunner.batch(sql,params);
}
@Test
public void test01() throws SQLException {
//使用DBUtils执行批处理
//二维数组:数组内的元素还是数组{{1,2},{3,4},{5,6}}
//批量往user表中添加五千条数据
//1:创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2. 编写SQL语句
String sql = "insert into users values(null,?,?,?)";
//3. 设置二维数组参数:第一维表示我们需要处理多少条数据,第二维表示sql语句有多少个参数
Object[][] params = new Object[5000][3];
for (int i = 0; i < params.length; i++) {
//params[i]表示插入的第i条数据
//params[i][1]表示插入的第i条数据的第一个问号处的参数
//注意数据库中下标从1开始 这里java从0开始
/* params[i][0] = "fbb" + i;
params[i][1] = "123456";
params[i][2] = "范冰冰" + i;
*/
}
//queryRunner执行批处理
queryRunner.batch(sql,params);
}
4.3:使用QueryRunner类实现查询
4.3.1:API介绍
1> query(String sql, ResultSetHandler<T> rsh, Object... params) ,执行查询 select
2>ResultSetHandler结果集处理类
4.3.2:Handler类型(处理结果集的工具类)
4.3.2:查寻的大体步骤
1. 创建QueryRunner,并且传入连接池对象
2:创建sql
3:调用查询额方法(sql,结果集处理器) 根据查询的不同场景选择结果集处理器
BeanHandler: 封装单个对象
BeanListHandler: 将多个对象封装到 List集合内
ScalarHandler 返回单个值 如果 结果集是多个值 可以采用下标或者是列的名称 进行灵活选择。
MapListHandler 存储的是 List<Map<String,Object>>
@Test
public void test03() throws SQLException {
//使用DBUtils执行查询的SQL语句,将查询到的一条数据封装到User对象中
//1. 创建QueryRunner,并且传入连接池对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2:创建SQL语句
String sql = "select * from users where id = ?";
//3:执行
User user = queryRunner.query(sql, new BeanHandler<>(User.class),1);
System.out.println("user = " + user);
}
@Test
public void test02() throws SQLException {
//查询多条数据,封装到List<JavaBean>
//1. 创建QueryRunner,并且传入连接池对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2:执行sql
String sql = "select * from users";
//BeanListHandler结果集处理器可以将结果封装称为一个个对象
List<User> list = queryRunner.query(sql, new BeanListHandler<>(User.class));
System.out.println("list = " + list);
}
@Test
public void test01() throws SQLException {
//查询数据条数
//1. 创建QueryRunner,并且传入连接池对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2:创建sql语句
String sql = "select count(id) from users";
//ScalarHandler 结果集处理器只能处理一条数据
Object query = queryRunner.query(sql, new ScalarHandler<>());
System.out.println("query = " + query);
}
五:分组结果集存储并查询
avg()
max()
等等
例如:select `job_id`,avg(salary) from t_employee group by did; 查询的字段不固定
此时需要使用 map 和 list ,map存储一条数据,字段名为key值为数据,多条数据放到list中
即需要使用maplistHandler()
@Test
public void test04() throws SQLException {
//1:创建queryrunner对象
QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
//2:创建sql
String sql = "select `job_id`,avg(salary) from t_employee group by did";
//3:执行sql
List<Map<String, Object>> query = queryRunner.query(sql, new MapListHandler());
//lambda表达式 方式一
/*query.forEach(new Consumer<Map<String, Object>>() {
@Override
public void accept(Map<String, Object> stringObjectMap) {
stringObjectMap.forEach((k,v)-> System.out.println(k + "==>" + v));
}
});*/
//lambda表达式 方式二 首先遍历list 因为list中的元素是map 因此进行第二次遍历 map
query.forEach((e) -> {
e.forEach((k,v) -> {
System.out.print(k + "<===>" + v + " ");
});
System.out.println();
} );
六:Idea自动生成实体类
时区不正确需要加上
?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC"
自动生成实体类![](https://i-blog.csdnimg.cn/blog_migrate/3d045d9a73d1ed7c092393e858af3f98.png)
注意:
在生成实体类的时候如果字段的名字中如果有下划线的话,要给sql语句的字段名起个别名,小驼峰的方式进行命名,否则将不能赋上值。