1.トランザクションの特性と分離レベル-(インタビューの質問)
- トランザクション特性ACID
- Atomicity:トランザクションが分割できない作業単位、トランザクション内の操作であることを示します。それがすべて起こるか、起こらないかのどちらかです。
- 一貫性:トランザクションでは、トランザクションの前後のデータの整合性が一貫している必要があります。
- 分離:複数のユーザーが同時にデータベースにアクセスする場合、1人のユーザーのトランザクションが他のユーザーのトランザクションによって妨害されることはなく、複数の同時トランザクション間のデータを互いに分離する必要があります。
- 耐久性:トランザクションがコミットされると、データベース内のデータへの変更は永続的であり、データベースに障害が発生してもトランザクションに影響はありません。
- 同時アクセスの問題-分離によって引き起こされる分離が
考慮されていない場合、トランザクションには3つの同時アクセスの問題があります。
- ダーティリード:トランザクションBは、トランザクションAによってまだコミットされていないデータを読み取りました------トランザクションBは、トランザクションAによって送信されたデータを読み取る必要があります
- 繰り返し不可の読み取り:トランザクションで2回読み取られたデータの内容に一貫性がない-トランザクションで複数回読み取られたときにデータに一貫性があることが要件-unpdate
- ファントム読み取り/仮想読み取り:トランザクションで2回読み取られるデータの量に一貫性がありません-----トランザクションで複数回読み取られるデータの量は同じである必要があります-挿入削除
3.トランザクションの分離レベル
read uncommitted
:送信されていないデータの読み取り:問題は解決できませんread committed
:送信されたデータを読み取ります:ダーティ読み取りは解決できます---- oracle defaultrepeatable read
:再読み取り読み取り:ダーティ読み取りと繰り返し不可能な読み取りを解決できます—mysqlのデフォルトserializable
:シリアル化:ダーティ読み取り、繰り返し不可能な読み取り、仮想読み取りを解決できます-ロックテーブルと同等です
パフォーマンスの分離レベル:
read uncommitted
> read committed
> repeatable read
>serialazable
セキュリティ:
read uncommitted
< read committed
< repeatable read
<serialazable
2、MySQLトランザクション
1.1。トランザクションとは
モノがn個の構成単位を持っているか、これらのn個の構成単位が同時に成功するか、n個の単位が同時に失敗します。n個のコンポーネントを1つのトランザクションに入れることです
2.2。mysql
トランザクションのデフォルトトランザクション:
SQLステートメントは、デフォルトでは、トランザクションを開いてコミットするためのトランザクションです。
手動トランザクション:
- 表示されたトランザクションを開きます:トランザクションを開始します
- トランザクションの送信:コミットは、トランザクションの開始からトランザクションの送信までのすべてのSQLが有効であると見なされ、データベースを真に更新することを表します。commitの書き込みを忘れた場合、トランザクションはデフォルトでロールバックされます
- トランザクションのロールバック:ロールバックはトランザクションのロールバックを表します。トランザクションの開始からトランザクションのロールバックまでのすべてのSQL操作は無効と見なされ、データベースは更新されていません。
ケース:
1。最初にデータベースを作成します。テーブルの名前はaccountです。
START TRANSACTION;
INSERT INTO account VALUES(NULL,'jack',5000);
COMMIT;
上記のsqlステートメントが実行され、トランザクションが正常に実行されます。
2.コミットを作成しなかった場合
START TRANSACTION;
INSERT INTO account VALUES(NULL,'lucy',5000);
次に、cmdウィンドウでデータを照会できますが、データはディスクに存在しませんが、ログファイルに出力されます。
3.トランザクションのロールバック
START TRANSACTION;
INSERT INTO account VALUES(NULL,'amy',5000);
ROLLBACK;
トランザクションがロールバックされているため、sqlステートメントを挿入できません
3、JDBCトランザクション操作
デフォルトは自動トランザクションです:
sqlステートメントを実行します。executeUpdate()---- executeUpdateメソッドが実行されるたびに、トランザクションが自動的に送信されます。
jdbc APIを介した手動トランザクション:
トランザクションを開きます:conn.setAutoComnmit(false);
トランザクションをコミットします:conn.commit();
トランザクションをロールバックします:conn.rollback();
注:制御トランザクションの接続は同じである必要があります
sqlを実行する接続とトランザクションを開始する接続は、トランザクションを制御するために同じである必要があります
//通过jdbc去控制事务
Connection connection=null;
//1.注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
connection= DriverManager.getConnection("jdbc:mysql:///web19","root","123");
//手动开启事务
connection.setAutoCommit(false);
//3.获取执行平台
Statement statement =connection.createStatement();
//4.操作sql
statement.executeUpdate("insert into account values(null,'zhangsan',6000)");
//statement.executeUpdate("insert into account values(null,'lisi',7000)");
//提交事务
connection.commit();
//关闭
statement.close();
connection.close();
}catch (Exception e) {
// TODO: handle exception
try {
connection.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
4、DBUtilsトランザクション操作
QueryRunner
パラメトリック構造:QueryRunner runner = new QueryRunner(DataSource dataSource);
パラメータ化された構造はデータソース(接続プール)をパラメータとしてQueryRunnerに渡します。QueryRunnerは接続プールからデータベース接続リソースを取得してデータベースを操作するため、Connectionパラメータなしでupdateメソッドを直接使用します。データベースを操作できます
パラメータ構造なし:QueryRunner runner = new QueryRunner();
パラメーターなしの構成では、データソース(接続プール)がパラメーターとしてQueryRunnerに渡されないため、QueryRunnerオブジェクトを使用してデータベースを操作する場合は、Connectionパラメーターを指定したメソッドを使用する必要があります。
簡単に言うと、sqlがトランザクション制御を必要とする場合、パラメーターなしの構造を使用し、パラメーター化された構造を使用するためにトランザクション制御を必要としません。
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
Connection conn=null;
try {
//获得一个Connection
conn =DataSourceUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//runner.update("update account set money=1000 where name='tom");
runner.update(conn, "update account set money=1000 where name='tom");
//提交或回滚事务
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
5、転送機能のケースを完了する
まず、MVCモードでWeb転送機能を分析します
最初のステップ:最初にjspを書く
<form action="${pageContext.request.contextPath}/transfer" method="post">
转出账户:<input type="text" name="out"><br/>
转入账户:<input type="text" name="in" ><br/>
转账金额:<input type="text" name="money"><br/>
<input type="submit" value="确认转账"><br/>
</form>
ステップ2:ページのデータを取得するWebレイヤーを作成し、サービスレイヤーのデータを呼び出します
。Webレイヤーはサーブレットです。自分でサーブレットを作成する必要があります。
@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接受转账的参数
String out = request.getParameter("out");
String in =request.getParameter("in");
String moneyStr =request.getParameter("money");
double money =Double.parseDouble(moneyStr);
//调用业务层的转账方法
TransferService service =new TransferService();
boolean isTransferSuccess= service.tranfer(out,in,money);
response.setContentType("text/html;charset=UTF-8");
if (isTransferSuccess) {
response.getWriter().write("转账成功!!!");
}else {
response.getWriter().write("转账失败!!");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
ステップ3:
daoレイヤーを作成し、データベース内のデータを処理して、サービスレイヤーにフィードバックします
public void out(Connection conn,String out, double money) throws SQLException {
QueryRunner queryRunner =new QueryRunner();
//Connection conn =DataSourceUtils.getConnection();
String sql ="update account set money =money-? where name=?";
queryRunner.update(conn,sql,money,out);
}
public void in(Connection conn,String in, double money) throws SQLException {
QueryRunner queryRunner =new QueryRunner();
//Connection conn =DataSourceUtils.getConnection();
String sql ="update account set money =money+? where name=?";
queryRunner.update(conn,sql,money,in);
}
ステップ4:
サービスレイヤーを作成する
public boolean tranfer(String out, String in, double money) {
//创建dao对象
TransferDao dao =new TransferDao();
boolean isTranferSuccess =true;
Connection conn=null;
try {
conn =DataSourceUtils.getConnection();
conn.setAutoCommit(false);
//转出方法
dao.out(conn,out,money);
//转入方法
dao.in(conn,in,money);
} catch (Exception e) {
isTranferSuccess =false;
//回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
conn.commit();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return isTranferSuccess;
}
6番目のアップグレードバージョン:ThreadLocalを使用して接続リソースをバインドします
トランザクションを使用するため、MVCモデルに準拠していません。上記のコードでは、接続はサービスレイヤーで使用されています。しかし、接続は、データベースに表示されます。daoレイヤーで使用する必要があります。したがって、ThreadLocalメソッドをお勧めします。
ThreadLocalメソッドは、スレッドの知識を使用します。この行を転送して、スレッドで完了します。
最初のステップ:
ツールの作成
public class MyDataSourceUtils {
//获取Connection------从连接池中获取
private static ComboPooledDataSource dataSource =new ComboPooledDataSource();
//创建ThreadLocal-------相当于一个map集合
private static ThreadLocal<Connection> tl =new ThreadLocal<Connection>();
//开启事务
public static void startTransaction() throws SQLException {
Connection connection= getCurrentConnection();
connection.setAutoCommit(false);
}
//获得当前线程上绑定的conn
public static Connection getCurrentConnection() throws SQLException {
//从ThreadLocal寻找当前线程是否有对应Connection
Connection conn =tl.get();
if (conn ==null) {
//获得新的connection
conn=getConnection();
//将conn资源绑定到ThreadLocal(map)上
tl.set(conn);
}
return conn;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//事务回滚
public static void rollback() throws SQLException {
getCurrentConnection().rollback();
}
//事务提交
public static void commit() throws SQLException {
getCurrentConnection().commit();
//将Connection从ThreadLocal中移除
tl.remove();
getCurrentConnection().close();
}
}
手順2:
サービスレイヤーを変更する
public class TransferService {
public boolean tranfer(String out, String in, double money) {
//创建dao对象
TransferDao dao =new TransferDao();
boolean isTranferSuccess =true;
Connection conn=null;
try {
//开启事务
//conn =DataSourceUtils.getConnection();
//conn.setAutoCommit(false);
//使用ThreadLocal存储Connection
MyDataSourceUtils.startTransaction();
//转出方法
dao.out(out,money);
//转入方法
//int i=1/0;
dao.in(in,money);
} catch (Exception e) {
isTranferSuccess =false;
//回滚事务
try {
MyDataSourceUtils.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
MyDataSourceUtils.commit();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return isTranferSuccess;
}
}
ステップ3:
daoレイヤーを変更する
public class TransferDao {
public void out(String out, double money) throws SQLException {
QueryRunner queryRunner =new QueryRunner();
Connection conn =MyDataSourceUtils.getCurrentConnection();
String sql ="update account set money =money-? where name=?";
queryRunner.update(conn,sql,money,out);
}
public void in(String in, double money) throws SQLException {
QueryRunner queryRunner =new QueryRunner();
Connection conn =MyDataSourceUtils.getCurrentConnection();
String sql ="update account set money =money+? where name=?";
queryRunner.update(conn,sql,money,in);
}
}
ThreadLocalはマップコレクションであることに注意してください。そのキーは主にスレッド名であり、値は接続に配置するだけで済みます。