如何使用jdbcTemplate和@Transactional等注释使用spring 4或更高版本在表上放置锁和事务?

时间:2017-04-07 09:03:47

标签: spring transactions jdbctemplate spring-java-config

我试图在桌子上写字时把锁放在桌子上,如果之间发生了什么事情,那么回滚。

尝试转换下面的代码

lock table test_g1 read;
lock table test_g write;

-- BEGIN;
START TRANSACTION;

insert into test_g1 values(143);
insert into test_g values(145);
select * from test_g1;
select * from test_g;
Rollback;

select * from test_g;
unlock tables;

如何将上面的代码转换为@Transactional spring jdbcTemplate代码?

@Transactional(rollbackFor={DataAccessException.class})
     public void Test(){
        jdbcTemplate.execute("insert into test1 (id, nam) values (4, 'A')");
        throw new DataAccessException("error") {
         };
     }

这里我试图抛出错误,所以insert语句应该回滚,但它没有发生。

由于

修改-1 我附上代码,我到底在做什么

在JdbcDaoImpl.java中,我在Test()上面提到了我的问题。

App.java

package com.cgiri.javabrains.Spring4;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.transaction.annotation.Transactional;

public class App 
{


    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        App2 app2 = ctx.getBean("app2",App2.class);
     app2.call();  
    }
}

App2.java

package com.cgiri.javabrains.Spring4;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class App2 {
    @Autowired
    private ApplicationContext ctx = null;

    JdbcDaoImpl jdbcDaoImpl ;

    public void call(  )
    {
        jdbcDaoImpl =  ctx.getBean("jdbcDaoImpl",JdbcDaoImpl.class);

        System.out.println(jdbcDaoImpl.getCount());
        try{
            jdbcDaoImpl.Test();
        }catch(Exception e)
        {

        }
        System.out.println(jdbcDaoImpl.getCount());


    }

    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.ctx = context;

    }
}

JdbcDaoImpl.java

package com.cgiri.javabrains.Spring4;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;



@Component
@Transactional
public class JdbcDaoImpl {

    private JdbcTemplate jdbcTemplate;
    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int getCount() {
        String sql = "SELECT COUNT(*) FROM test1";
        // jdbcTemplate.setDataSource(getDataSource());
        return jdbcTemplate.queryForObject(sql, Integer.class);
    }

    public void crateTable() {
        String sql = "create table if not exists test1 (id integer, nam char(50))";
        jdbcTemplate.execute(sql);
        jdbcTemplate.execute("insert into test1 (id, nam) values (1, 'A')");
        int count = jdbcTemplate.queryForObject("select count(*) from test1",Integer.class);    

        System.out.println(count);
    }



    /****  This is the point where i am trying to rollback insert query ,but it's not happening , instead it's inserting the data into the table and just throwing exception , rollback is not happeneing  ****/  
    @Transactional(rollbackFor={DataAccessException.class})
    public void Test(){
        jdbcTemplate.execute("insert into test1 (id, nam) values (4, 'A')");
        throw new DataAccessException("error") {
        };
    }

}

AppConfig.java

package com.cgiri.javabrains.Spring4;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;

@Configuration
@ComponentScan({ "com.cgiri.javabrains.Spring4" })
@PropertySource("classpath:db.properties")
public class AppConfig {

    private JdbcTemplate jdbcTemplate;

    private TransactionTemplate transactionTemplate;

    @Autowired
    private Environment env;


    @Bean
    public BasicDataSource getBasicDataSource()
    {
        BasicDataSource dao = new BasicDataSource();
        dao.setDriverClassName(env.getProperty("db.driverClassName"));
        dao.setUrl(env.getProperty("db.url"));
        dao.setUsername(env.getProperty("db.userName"));
        dao.setPassword(env.getProperty("db.password"));
        dao.setInitialSize(2);
        dao.setMaxActive(5);
        return dao;
    }

    @Bean
    public DataSourceTransactionManager getTransactionManager(BasicDataSource dataSource) {
        DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
        return manager;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }



}

为了在DML查询期间锁定表,如果有两个或更多人同时在表上更新,这个锁定机制由mysql服务器负责,或者我们必须单独配置我们为事务做的方式?

谢谢

1 个答案:

答案 0 :(得分:1)

如果您不想使用@Transactional,那么您可以尝试使用TransactionTemplate之类的内容:

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@Component
public SimpleDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private TransactionTemplate transactionTemplate;

    private void executeTransactionWithoutResult(DbTransactionTask dbTask) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                dbTask.executeTask();
            }
        });
    }

    public void test() {
        DbTransactionTask dbTask = new DbTransactionTask() {
            @Override
            public void executeTask() {
                try {
                    jdbcTemplate.execute("LOCK TABLES Entry WRITE;");
                    jdbcTemplate.execute("...");
                    jdbcTemplate.execute("...");
                    jdbcTemplate.execute("UNLOCK TABLES;")
                } catch (Exception e) {
                    // Cause rollback of transaction
                    throw new RuntimeException("Reverting DB operations: " + e.getClass().getSimpleName() + " - " + e.getMessage(), e);
                }
            }
        };

        executeTransactionWithoutResult(dbTask);    
    }

    abstract class DbTransactionTask { public abstract void executeTask(); }
}

编辑:使用@Transactional您可以尝试以下内容:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.transaction.annotation.Transactional;

import dao.EntryDao;

@SpringBootApplication
public class SpringTransactional {
    private ConfigurableApplicationContext springContext;

    @Autowired
    private EntryDao dao;

    public void init() {
        springContext = SpringApplication.run(SpringTransactional.class);
        springContext.getAutowireCapableBeanFactory().autowireBean(this);
    }

    public static void main(String[] args) {
        SpringTransactional st = new SpringTransactional();
        try {
            st.init();
            dao.db_transaction_test();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            st.springContext.close();
        }
    }
}

EntryDao 是:

package dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * DB creation and schema:
 * CREATE DATABASE db_name;
 * CREATE USER db_username;
 * <p>
 * USE db_name;
 * GRANT ALL ON db_name.* TO db_username;
 * <p>
 * SET PASSWORD FOR spz = PASSWORD('username123');
 * FLUSH PRIVILEGES;
 * <p>
 * CREATE TABLE Entry (
 * entry_ID INT NOT NULL AUTO_INCREMENT,
 * name   TEXT NOT NULL,
 * <p>
 * PRIMARY KEY (entry_ID)
 * );
 */
@Component
public class EntryDao {
    /**
     * application.properties:
     * spring.datasource.driver-class-name = com.mysql.jdbc.Driver
     * spring.datasource.url = jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC
     * spring.datasource.username = db_username
     * spring.datasource.password = username123
     */
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void db_transaction_test() {
        jdbcTemplate.execute("LOCK TABLES Entry WRITE;");

        for (int i = 0; i < 10; i++) {
            try {
                int entry_name = getEntryId("entry_" + i);
                System.out.println("Created entry id=" + entry_name);
            } catch (EntryDao.DaoException e) {
                e.printStackTrace();
            }

            if (i == 5) {
                throw new RuntimeException("Testing data upload procedure break.");
            }
        }

        jdbcTemplate.execute("UNLOCK TABLES;")
    }

    public int getEntryId(String entryName) throws DaoException {
        List<DbEntry> dbEntries = retrieveEntriesFor(entryName);

        if (dbEntries.size() == 1) {
            return dbEntries.get(0).getEntry_ID();
        } else if (dbEntries.size() == 0) {
            String sqlInsert = "INSERT INTO Entry (name) VALUES (?)";
            jdbcTemplate.update(sqlInsert, entryName);
            dbEntries = retrieveEntriesFor(entryName);
            if (dbEntries.size() == 1) {
                return dbEntries.get(0).getEntry_ID();
            } else {
                throw new DaoException("Invalid results amount received after creating new (" + dbEntries.size() + ") when getting entry for name: " + entryName);
            }
        } else {
            throw new DaoException("Invalid results amount received (" + dbEntries.size() + ") when getting entry for name: " + entryName);
        }
    }

    private List<DbEntry> retrieveEntriesFor(String entryName) {
        return jdbcTemplate.query("SELECT * FROM Entry WHERE name=?;", (ResultSet result, int rowNum) -> unMarshal(result), entryName);
    }

    private DbEntry unMarshal(ResultSet result) throws SQLException {
        DbEntry dbEntry = new DbEntry();
        dbEntry.setEntry_ID(result.getInt("entry_ID"));
        dbEntry.setName(result.getString("name"));
        return dbEntry;
    }

    public class DbEntry {
        private int entry_ID;
        private String name;

        int getEntry_ID() { return entry_ID; }
        void setEntry_ID(int entry_ID) { this.entry_ID = entry_ID; }
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
    }

    public class DaoException extends Throwable { DaoException(String err_msg) { super(err_msg); } }
}