mysql内置数据库:
information_schema数据库:
其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。performance_schema 数据库:
存储引擎:命名PERFORMANCE_SCHEMA ,主要用于收集数据库服务器性能参数。- mysql 数据库:
mysql库是系统库,里面保存有账户信息,权限信息,存储过程,event,时区等信息。
test 数据库:
这个是安装时候创建的一个测试数据库,和它的名字一样,是一个完全的空数据库,没有任何表,可以删除。
连接池:
Sun公司在jdbc2.0规范中提供了一个新的接口javax.sql.DataSource来实现连接池。
数据库连接池的实现步骤:
1. 我们自定义的数据库连接池需要实现javax.sql.DataSource这个接口。这是sun公司规定。
备注: DriverManager 是JDBC 1.0规范,可以创建连接,但是不支持连接池
DataSource是JDBC 2.0规范,支持连接池
我们使用linkedList来存放连接。当做连接池。
2. 在dataSource的构造方法中初始化数据库连接池。向池子中创建一定数量的数据库连接对象。
3. 重写DataSource这个接口中的getConnection方法。注意:这个方法是从我们数据库连接池中获取数据,从连接池中获取之后需要将池子中的对象给删掉
4. 提供释放资源的方法。注意:这个释放资源是将连接放回连接池中,而不是关闭连接
/**
* 学习内容: 自定义连接池
* 1.实现javax.sql.DataSource接口
* 2.创建容器LinkedList
* 3.在构造方法中创建一些连接
* 4.实现DataSource接口中的getConnection方法,从容器中获取
* 5.将使用完毕的连接放回容器
*/
public class MyDataSource implements DataSource {
//arraylist 查询快,增删慢,LinkedList增删快,连接主要做增删
private static LinkedList<Connection> pool=new LinkedList<Connection>();
public MyDataSource() {
super();
//向容器添加一些连接
for(int i=0;i<10;i++){
Connection connection = JdbcUtil.getConnection();
pool.addFirst(connection);
}
System.out.println("创建连接,默认往连接池中添加了"+pool.size()+"个连接");
}
@Override
public Connection getConnection(){
if(pool.isEmpty()){
for(int i=0;i<3;i++){
Connection connection = JdbcUtil.getConnection();
pool.addFirst(connection);
}
}
final Connection connection = pool.removeLast();
System.out.println("从连接池中获取连接,当前还有"+pool.size()+"个连接");
ClassLoader loader=connection.getClass().getClassLoader();
Class<?>[] interfaces=connection.getClass().getInterfaces();
InvocationHandler h=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if("close".equals(method.getName())){
//增强close方法,将连接放回连接池
backToPool(connection);
System.out.println("执行了动态代理的方法,将连接放回了连接池,当前还有"+pool.size()+"个连接");
return null;
}
//如果不是close方法,不做处理,让被代理类本身执行方法即可
return method.invoke(connection, args);
}
};
//返回连接的代理类
Connection proxyConn = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
return proxyConn;
}
//定义一个将连接放回容器的方法
public void backToPool(Connection connection){
pool.addFirst(connection);
System.out.println("将连接放回连接池,当前还有"+pool.size()+"个连接");
}
}
/**
* 2个问题需要优化:
*
* 1.dataSource是一个接口,接口接收实现类,来解耦,提高扩展性
* MyDataSource dataSource = new MyDataSource();改为DataSource dataSource = new MyDataSource()
*
* 2.dataSource是sun规定的规范,我们不应该自己定义一些不同的api,程序员不用额外地记忆新的api,程序员习惯了使用close方法去释放连接,
* 那么数据库连接池也应该调用连接的close方法来将连接放回连接
*/
public static void main(String[] args){
DataSource dataSource = null;
Connection connection = null;
PreparedStatement prepareStatement = null;
ResultSet resultSet = null;
try {
dataSource = new MyDataSource();
connection = dataSource.getConnection();
String sql="select * from User";
prepareStatement = connection.prepareStatement(sql);
resultSet = prepareStatement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
int age = resultSet.getInt("age");
System.out.println(id+username+password+age);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtil.release(resultSet, prepareStatement, null);
}
}
Dbutils三个核心类功能介绍
- QueryRunner中提供对sql语句操作的API.
- ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
java.lang.ThreadLocal 该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具类底层就是一个Map,key存放的当前线程,value存放需要共享的数据。如下图,在整个程序运行中,我们有个Map变量,每条线程又有属于自己的id,也就是thread.currentThread。所以我们可以向map中存储一个值,key就是当前线程的id,值就是我们需要存储的值。这样,我们在整个线程中的任何一个地方,我们可以通过当前线程的id从map中获取的值都是同一个。这样,我们在一条线程中,我们在service层和dao层从map中就能获取同一个connection对象了。
事务 (ThreadLocal版本转账案例)
public class ThreadLocalC3p0JdbcUtil {
//ComboPooledDataSource构造方法默认会去找src/c3p0-config.xml 这个配置文件
//之前的获取连接是采用DriverManager类实现的 静态代码块读取配置文件 没有使用开源数据源
//这里的获取连接是采用开源连接池获取数据源 改写了原来获取连接的方法
private static DataSource datasource=new ComboPooledDataSource();
//创建一个位置存放线程的Connection
private static ThreadLocal<Connection> local=new ThreadLocal<Connection>();
//获取数据源
public static DataSource getDataSource(){
return datasource;
}
//注册和获取连接
public static Connection getConnection(){
//从指定的位置获取连接
Connection connection = local.get();
//第一次获取肯定是null,还没存放
if(connection==null){
try {
connection=datasource.getConnection();
//第一次将连接放置到指定位置
local.set(connection);
System.out.println("第一次创建,放进入到ThreadLocal中:"+connection);
} catch (SQLException e) {
e.printStackTrace();
}
}else{
System.out.println("不是第一次创建 直接从ThreadLocal中获取:"+connection);
}
return connection;
}
//资源释放
//如果使用DbUtils包中QueryRunner类来crud的时候是不需要释放连接的 也就是说下面的方法不需要使用 如果没有使用DbUtils那么还是需要使用下面的方法释放资源的
public static void release(ResultSet resultSet,Statement statement, Connection connection){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
resultSet = null;
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
statement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
connection = null;
}
}
//释放threadlocal的值
public static void remove() {
//remove时有没有直接将其他的线程的连接删除???
local.remove();
}
}
public class ThreadLocalWebApp {
public static void main(String[] args) {
//转账业务
//jack给rose转账100
String outer = "jack";
String inner = "rose";
double money = 100;
ThreadLocalAccountService service = new ThreadLocalAccountService();
boolean result = service.tranfer(outer,inner,money);
// new Thread(){
// @Override
// public void run() {
// String outer = "jack";
// String inner = "rose";
// double money = 100;
//
// ThreadLocalAccountService service = new ThreadLocalAccountService();
// boolean result = service.tranfer(outer,inner,money);
// }
// }.start();
if (result) {
System.out.println("转账成功");
}else{
System.out.println("转账失败");
}
}
}
public class ThreadLocalAccountService {
public boolean tranfer(String outer, String inner, double money) {
ThreadLocalAccountDao dao=new ThreadLocalAccountDao();
//这里获取的连接是当前线程的连接
Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
try {
//使用连接开启事务(注意,如果sql要被事务进行管理,那么sql必须与开启事务的连接是同一个)
//问题:当前开启事务的连接与sql执行的连接是同一个么???
connection.setAutoCommit(false);//关闭自动提交,开启事务
dao.outMoney(outer,money);
//转账流水等业务操作
int i = 1/0;
dao.inMoney(inner, money);
//业务成功,提交事务
//connection.commit();
DbUtils.commitAndClose(connection);
return true;
} catch (Exception e) {
e.printStackTrace();
DbUtils.rollbackAndCloseQuietly(connection);
}finally{
ThreadLocalC3p0JdbcUtil.remove();
}
return false;
}
}
public class ThreadLocalAccountDao {
//dao层用来让对应的账户减钱
public int outMoney(String outer, double money) throws Exception {
QueryRunner runner = new QueryRunner();
String sql="update account set money=money-? where name=?";
//从指定的位置去获取连接
Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
int updatecount = runner.update(connection,sql, money,outer);
return updatecount;
}
//dao层用来让对应的账户加钱
public int inMoney(String inner, double money) throws Exception {
QueryRunner runner = new QueryRunner(C3p0JdbcUtil.getDataSource());
String sql="update account set money=money+? where name=?";
//从指定的位置去获取连接
Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
int updatecount = runner.update(connection,sql, money,inner);
return updatecount;
}
}
事务的特性 ACID
数据库的事务必须具备ACID特性,ACID是指 Atomicity(原子性)、Consistensy(一致性)、Isolation(隔离型)和Durability(持久性)的英文缩写。
原子性(Atomicity)
事务包装的一组sql,要么都执行成功,要么都失败。这些操作是不可分割的。
一致性(Consistency)
数据库的数据状态是一致的。
事务的成功与失败,最终数据库的数据都是符合实际生活的业务逻辑。一致性绝大多数依赖业务逻辑和原子性。
持久性:(Durability)
事务成功提交之后,对于数据库的改变是永久的。哪怕数据库发生异常,重启之后数据亦然存在。
隔离性(Isolation)
一个事务的成功或者失败对于其他的事务是没有影响。2个事务应该相互独立。
事务的隔离级别
如果不考虑事务的隔离性,由于事务的并发,将会出现以下问题:
1、脏读 – 最严重,杜绝发生
2、不可重复读
3、幻读(虚读)
隔离级别:解决问题
* 数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。
1. 存在:3个问题(脏读、不可重复读、虚读)。
2. 解决:0个问题
2. read committed 读已提交,一个事务读到另一个事务已经提交的数据。
1. 存在:2个问题(不可重复读、虚读)。
2. 解决:1个问题(脏读)
3. repeatable read:可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。
1. 存在:1个问题(虚读)。
2. 解决:2个问题(脏读、不可重复读)
4. serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。
1. 存在:0个问题。
2. 解决:3个问题(脏读、不可重复读、虚读)
* 安全和性能对比
* 安全性:serializable > repeatable read > read committed > read uncommitted
* 性能 : serializable < repeatable read < read committed < read uncommitted
* 常见数据库的默认隔离级别:
* MySql:repeatable read
* Oracle:read committed
在mysql数据库中,底层对于幻读做了优化,演示不了。