MySql - 基础学习8 - mysql拓展
一.SQL注入的问题
sql存在漏洞,会被攻击和数据泄露(statement对象)
SQL语句会被自动拼接 如果输入字符有 OR 那可以保证后面数据永远为真,例如:‘ ’(空字符)or ‘ 1 = 1 ’
以上这样的语句会导致sql语句拼接时候出错后面永远为真,所以前面输入什么都没关系,整体都为真
二.preparedStatement对象(防sql注入)
使用PreparedStatement对象来执行java所编写的sql语句可以防止sql注入的问题,比本来的Statement对象的执行更加安全
使用PreparedStatement对象插入
public static void main(String[] args) { Connection conn=null; PreparedStatement st =null; ResultSet rs=null; try { conn=JdbcUtils.getConnection(); String sql="insert into `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";//不放入值,用?占位 st=conn.prepareStatement(sql);//sql 预编译 st.setInt(1,4); st.setString(2,"maming"); st.setString(3,"112233"); st.setString(4,"asdf@1234"); // 这里的最外面new 的对象是要插入的sql格式的时间,而里面new的Date()是java对象所含的现在时间 st.setDate(5,new java.sql.Date(new Date().getTime())); int i=st.executeUpdate(); if(i>0){ System.out.println("PreparedStatement对象插入成功!"); } } catch (SQLException e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } }
使用PreparedStatement对象查找
public class TestSelectPro { public static void main(String[] args) { Connection conn=null; PreparedStatement st=null; ResultSet rs=null; try { conn=JdbcUtils.getConnection(); String sql ="select * from `users` where id=?"; st = conn.prepareStatement(sql); st.setInt(1,2); rs= st.executeQuery(); while(rs.next()){ System.out.println("name="+rs.getString("NAME")); } } catch (SQLException e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
区别:
- PreparedStatement对象使用了 ? 作为占位符
- 需要预编译sql,先写sql语句和拿到PreparedStatement对象,不执行
- 手动给参数赋值,(第几个问号,赋什么值)
- 执行
本质:PreparedStatement对象放注入的本质就是把传进来的参数当作了字符,里面的 ‘ 就会被直接转义,就不会发生那种从客户端输入一句sql的情况了
Java实现事务:
ACID原则:
原子性:要么完成,要么都不完成
一致性:总数不变
持久性:一旦提交不可逆,持久化到数据库
隔离性:多个进程互不干扰
隔离性产生的问题:
脏读:一个事务读取了另一个欸有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据
public class TestTrunsation { public static void main(String[] args) { Connection conn =null; PreparedStatement st =null; ResultSet rs=null; try { conn=JdbcUtils.getConnection(); conn.setAutoCommit(false);// 关闭自动提交,Java默认开启事务 String sql1 ="update account set money= money -100 where name = 'A'"; st=conn.prepareStatement(sql1); st.executeUpdate(); String sql2 ="update account set money= money +100 where name = 'B'"; st=conn.prepareStatement(sql2); st.executeUpdate(); // 业务成功,提交 conn.commit(); System.out.println("成功!"); } catch (SQLException e) { try { // 失败就回滚 conn.rollback(); System.out.println("失败!"); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
代码实现:
- 开启事务:conn.setAutocommit()
- 一组业务执行完毕,提交事务
- 可以在catch中显示增加回滚,java默认也会事务回滚
三.数据库连接池
数据库连接 ----- 执行完毕 ----释放
直接连接了,然后用一次立马释放太浪费资源了,所以衍生出了池化技术
池化技术:准备一些预先资源,过来连接预先准备好的
关键词:
最小连接数:10
最大连接数:15(数字根据具体来)
等待超时:100ms
编写连接池,实现一个接口:DataSource
开放数据源实现:DBCP,C3P0,Druid(阿里巴巴)
DBCP:导包(我选择maven)
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.2</version> </dependency>
数据源:dbcpconfig.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=123456 #<!-- 初始化连接 --> 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_COMMITTED
DBCP工具类:
public class JdbcUtils_DBCP {
private static DataSource dataSource=null;
static {
try{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.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 re){
if(re!=null){
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试DBCP
public class TestDBCP {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st =null;
ResultSet rs=null;
try {
conn= JdbcUtils_DBCP.getConnection();
String sql="insert into `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";//不放入值,用?占位
st=conn.prepareStatement(sql);//sql 预编译
st.setInt(1,7);
st.setString(2,"maming");
st.setString(3,"112444");
st.setString(4,"asdf@1234");
// 这里的最外面new 的对象是要插入的sql格式的时间,而里面new的Date()是java对象所含的现在时间
st.setDate(5,new java.sql.Date(new Date().getTime()));
int i=st.executeUpdate();
if(i>0){
System.out.println("PreparedStatement对象插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_DBCP.release(conn,st,rs);
}
}
}
完结撒花!