Spring Boot學習6:Spring Boot JDBC

一枚程式設計師發表於2018-02-13
1 資料來源
資料來源是資料庫連線的來源,通過DataSource介面獲得

資料來源型別
1)通用型資料來源(javax.sql.DataSource)
主要使用場景:通用型資料庫,本地事務,一般通過Socket方式連線

2)分佈型資料來源(javax.sql.XADataSource)
主要使用場景:通用型資料庫,分散式事務,一般通過Socket方式連線

3)嵌入式資料來源(org.springframework.jdbc.datasource.embedded.EmbeddedDatabase)
主要使用場景:本地檔案系統資料庫,如HSQL,H2,Derby等
列舉:org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType

2 事務
事務用於提供資料完整性,並在併發訪問下確保資料檢視的一致性


例子:建立springboot專案,並引用包web,jdbc,mysql驅動
建立專案後,我們看一下幾個介面Driver,DriverManager,Connection,Statement,ResultSet
我們來建立資料庫連線
建立JdbcController

@RestController
public class Jdbc2Controller {

    @Autowired
    @Qualifier(value="dataSource")
    private DataSource dataSource;

    @RequestMapping("/user/getUsers")
    public Map<String,Object> getUsers(@RequestParam(name="id",defaultValue = "1") String id){
        Map<String,Object> data = new HashMap<String,Object>();

        Connection connection=null;
        Statement statement = null;
        try {
            connection = dataSource.getConnection();
            statement = connection.createStatement();
            String sql = "select * from user where id="+id;
//            connection.prepareStatement()
            ResultSet resultSet = statement.executeQuery(sql);
            while(resultSet.next()){
                int r_id = resultSet.getInt("id");
                String r_name = resultSet.getString("name");
                int r_age = resultSet.getInt("age");
                data.put("id",r_id);
                data.put("name",r_name);
                data.put("age",r_age);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            try{
                connection.close();
                statement.close();
            }catch(SQLException e){
                e.printStackTrace();
            }
        }
        return data;
    }
}

配置jdbc,在application.properties中配置:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver


測試:
http://localhost:8080/user/getUsers?id=1
返回:
{"name":"xiaohuang","id":1,"age":1}



修改為防止sql注入的方式

@RestController
public class Jdbc2Controller {

    @Autowired
    @Qualifier(value="dataSource")
    private DataSource dataSource;

    @RequestMapping("/user/getUsers")
    public Map<String,Object> getUsers(@RequestParam(name="id",defaultValue = "1") int id){
        Map<String,Object> data = new HashMap<String,Object>();

        Connection connection=null;
        Statement statement = null;
        try {
            connection = dataSource.getConnection();
            String sql = "select id,name,age from user where id = ?";

            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            ResultSet resultSet = preparedStatement.executeQuery();
            while(resultSet.next()){
                int r_id = resultSet.getInt("id");
                String r_name = resultSet.getString("name");
                int r_age = resultSet.getInt("age");
                data.put("id",r_id);
                data.put("name",r_name);
                data.put("age",r_age);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            try{
                connection.close();
            }catch(SQLException e){
                e.printStackTrace();
            }
        }
        return data;
    }
}


3.JDBC4.0

例子:使用事務操作

package com.segmentfault.springbootlesson7;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import pojo.User;

import java.sql.PreparedStatement;
import java.sql.SQLException;

@Service
@EnableTransactionManagement
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    @Transactional(propagation = Propagation.REQUIRED,isolation= Isolation.SERIALIZABLE)
    public String save(User user) {
        Object obj = jdbcTemplate.execute("insert into user (name,age) values(?,?)", new PreparedStatementCallback<Object>() {
            @Override
            public Boolean doInPreparedStatement(PreparedStatement preparedStatement) throws SQLException, DataAccessException {
                preparedStatement.setString(1,"zhejaing");
                preparedStatement.setInt(2,100);
                return preparedStatement.executeUpdate()>0;
            }
        });
        return obj.toString();
    }
}


例子:使用jdbc獲取到資料庫中的後設資料以及資料

@Autowired
@Qualifier(value="dataSource")
private DataSource dataSource;

@Autowired
private JdbcTemplate jdbcTemplate;

@RequestMapping("/user")
public List<Map<String,Object>> getUser(){
    List<Map<String,Object>> data = jdbcTemplate.execute("select * from user", new PreparedStatementCallback<List<Map<String,Object>>>() {
        @Override
        public List<Map<String, Object>> doInPreparedStatement(PreparedStatement preparedStatement) throws SQLException, DataAccessException {
            ResultSet resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            List<String> columnNameList = new ArrayList<String>(columnCount);
            for(int i=1;i<=columnCount;i++){
                String columnName = metaData.getColumnName(i);
                columnNameList.add(columnName);
            }

            List<Map<String,Object>> data = new LinkedList<>();
            while(resultSet.next()){
                Map<String,Object> columnData = new LinkedHashMap<>();
                for(String columnName:columnNameList){
                    Object columnValue = resultSet.getObject(columnName);
                    columnData.put(columnName,columnValue);
                    data.add(columnData);
                }
            }
            return data;
        }
    });
    return data;
}

執行測試

[{"id":1,"name":"xiaohuang","age":1},{"id":1,"name":"xiaohuang","age":1},{"id":1,"name":"xiaohuang","age":1},{"id":2,"name":"xiaolong","age":22},{"id":2,"name":"xiaolong","age":22},{"id":2,"name":"xiaolong","age":22},{"id":3,"name":"xiaolong","age":23},{"id":3,"name":"xiaolong","age":23},{"id":3,"name":"xiaolong","age":23},{"id":4,"name":"John","age":18},{"id":4,"name":"John","age":18},{"id":4,"name":"John","age":18}]


還需要注意Transactional的底層原始碼,可以自行再次除錯學習

使用Transactional,會自動封裝物件,並強化事務功能


啟動代理有兩種,

介面的程式碼,是jdk的預設代理proxy,

@Autowired
private UserService userService;

@Service
@EnableTransactionManagement(proxyTargetClass = false)
public class UserServiceImpl implements UserService {

對類進行代理,那麼需要使用到cglib

@Autowired
private UserServiceImpl userService;
@Service
@EnableTransactionManagement(proxyTargetClass = true)
public class UserServiceImpl implements UserService {

4.微服務的分散式事務

使用Bitronix,

在start.spring.io上建立專案,並引用相關包


我們可以瞭解一下JTA相關的知識

http://blog.csdn.net/habren/article/details/52090773

https://www.ibm.com/developerworks/cn/java/j-lo-jta/


兩階段提交原理


tomcat中,分散式事務需要使用原始方法,UserTransaction, UserTransactionManager






5.問題

tomcat下面跨多個資料庫的分散式事務有什麼好的方法和建議?

UserTransaction,UserTransactionManager等了解下


如何高效學習JSR規範,有哪些規範值得關注?

Web:Servlet,WebSocket,WebService等

最重要的是Java JDBC規範,Java虛擬機器規範等

JSR規範主要:

DI

JaxB

JDBC

JMX(事務,點對點,高階協議)

JPA

JSTL

JSP

JTA

LANG

REST

SAX

SOAP

WEBSERVICE

WEBSOCKET

XML

WSDL



3)問題,Spring事務的底層實現,遇到RuntimeException才會回滾,為什麼?

因為SpringBoot裡面基本上都是執行時異常,但是Transactional註解可以指定哪些異常可以被回滾,哪些異常不回滾等


4)springboot動態切換資料來源的解決方案?


5)jdbc的connection和Orm比如Hibernate中的session什麼關係?如果將Hibernate的一級快取關了?

connection無狀態,session有狀態。

Hibernate的一級環境可以通過程式碼的方式關閉,不可通過配置方式關閉。


6)手動事務

PlatformTransactionManager事務管理器

TransactionStatus事務狀態

例子:

@Autowired
private PlatformTransactionManager platformTransactionManager;
public Boolean save2(User usr){
    DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
    Boolean result = null;
    try {
        result = save(usr);
        platformTransactionManager.commit(transactionStatus);
    } catch (Exception e) {
        e.printStackTrace();
        platformTransactionManager.rollback(transactionStatus);
    }
    return result;
}


相關文章