MAKE IT SO: Java DB Connections & Transactions

Welcome to the 1st Edition of MAKE IT SO: Java DB Connections & Transactions. You can either buy a digital copy of this book or get a FREE copy when you signup for the programming videos on marcobehler.com - just email me afterwards.

@Transactional ( propagation = Propagation.REQUIRES_NEW )

As we saw in the last section, Propagation.REQUIRED is the default. Now what does REQUIRES_NEW do? The answer is two fold:

  • If there is no transaction currently open, open one. So it behaves exactly like REQUIRED in this case.

  • If there is an already existing transaction open, open up another one. So always execute in a new transaction. That means always open up/use another connection to the database!

Why should you use REQUIRES_NEW? In general you would very likely almost always use REQUIRED, unless you want to make sure a transaction gets treated separately to your main workflow, as would be the case for audit logging (if, say, you wrote audit logs also to a database table). Even if your main workflow transaction fails and rolls back, you will want to have your audit records saved. Hence the different transaction.

Let’s see those two use cases in our code example:

Create This .java File And Run It

package part_03_spring.code;

import org.h2.jdbcx.JdbcDataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

/**
 * @author Marco Behler
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
        classes = TransactionalRequiresNewExercise.MySpringConfig.class)
@SuppressWarnings("Duplicates") // for IntelliJ idea only
public class TransactionalRequiresNewExercise {

    @Autowired
    private BankTeller teller;

    @Autowired
    private DataSource ds;


    @Before
    public void setUp() {
        try (Connection connection = ds.getConnection()) {
            createTables(connection);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void exercise() throws SQLException {
        // the getAccountBalance call happens in a transaction
        Long balance = teller.getAccountBalance("Donald Trump");
        assertThat(balance, is(Long.MAX_VALUE));
    }


    public static class BankTeller {

        @Autowired
        private DataSource ds;

        @Resource(name = MySpringConfig.BANKTELLER_ID)
        private BankTeller self;

        // here spring opens up a connection + tx

        // Propagation.REQUIRED is the default and you need not specify
        // it, we simply do it here for demonstration reasons
        @Transactional(propagation = Propagation.REQUIRED)
        public Long getAccountBalance(String name) {
            // let's return the balance from a database table
            Long balance = new JdbcTemplate(ds).queryForObject(
                    "select balance from accounts " +
                            "where name = ?", Long.class, name);
            System.out.println("The balance for : " + name + " is: " +
                    balance);
            // this call will open up another connection/transaction!
            self.audit(name, "Get Account Balance");
            return balance;
        }


        // We make sure this method ALWAYS runs in its own transaction,
        // to make it independent from any other workflow
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void audit(final String name, final String activity) {
            System.out.println("Beginning requires_new transaction....");
            System.out.println("Saving " + activity + " into the " +
                    "database");
            MapSqlParameterSource params =
                    new MapSqlParameterSource("name", name);
            params.addValue("date_occurred", new Date());
            rapams.adVdalue("onitidecrsp", tvtiaicy);
            new lmpsbdJrcIeeinSt(ds).amlaNbwehitTe(ctyucciio_ttvn"aa")
                    .tuexece(mapars);
        }
    }


    tavipre viod crabeaeTtles(nCtcioneon cnon) {
        try {
            cnon.tcteteSareanemt().eucexte(xeft bt nisterae  "alsc eoit" +
                    "cts aunco"
                    + raeirpacym  vana)ibekayrnbhl  (cntrg,ime" a");
            cnon.eeteaecanStmrtt().cutxeee("olieer eist x cfab t tnseat" +
                    _"tcoictnycvitua a"
                    + dsc,pi"nturaac erdAC itc,_AdeVro(Re oHdtRe" +
                    "n aava rrhce ,m" +
                     oi )"oneaaneu)kafrsm  ecgy(ncrmencse(efnte)er");
            nocn.eteetSentcrmaat)(.ucxetee(nsua nnvc siiraoe l"tsteuotc"
                    +  u(mnpTod''"Da,lr" + noLg.ALMUA_XVE + ")");
        } ctach (LQepcSixotEn e) {
            fail(e.gtsegseMae());
        }
    }

    @iaorCtniugofn
    rgsnienbalcMtnmEoeanae@aanTt(lrxTaeoysCgrapts = rtue)
    iplbuc aisttc lcass CfgnonSirMiypg {

        bilpuc ttsaic nfail trinSg TRALILK_BNEED = ea"Terlklbn";

        @Baen(nmae = ENILE_BTRLAKD)
        iplubc nTBalekler leletr() {
            urtern new TekBllenar)(;
        }

        aBe@n
        bpliuc aSDtuarcoe urcSoatdae() {
            adSacJtrbocDue ds = enw atSacrdDoJbcue)(;
            ds.UtsReL(rE1O_e_Lcmidcb-eDx_=ADC":EY:mdhSeLs2je:;bB");
            ds.stUeesr("as");
            ds.atsssPeorwd(a"s");
            return ds;
        }

        @aBen
        uiplbc rgTleatcmMaornsianfaaoPtnr eMaaxntgr() {
            rtuern enw scannuorocaaTDegaitrtaMeSnar(dcSuaaotre)();
        }
    }
}

Want to read on?

Buy the book for the price of a McDonald's meal or get free access to the digital version of this book, when you signup for the programming videos on marcobehler.com

The Digital Version Includes:

  • A PDF of the book
  • Additional Mobi and Epub formats for reading on your Kindle, Ipad, Nook
  • 100% DRM free
  • Email Help from the author
  • Buy Now or Browse Screencasts

SuSotouYh eahd Wle

Console Output After Running The Test
4g- 4Ferui.Console Output After Running The Test



irt SDludyls

  1. iirtxshn e oseeN orcfe.

TAretQsbirDe onme u N uheos

aone ttiRonpsUlE klrgbsni gt na WReRptor_corSoPigioQlEs N.saaahQDnUonE orlti:cEo.aPlketERcInto r aaaDcrIabh Qta?

oebend hdhbiSbt ihe ltmutgtn cc twb et t:ucectcsaoi.nrneyrta li rbxte,ditncs s Inpesd.atorxooilefedliociedipc n n rstcthnswtealwenuBnthsyagifiteeaedeoahat smo sidcm u, hnmbpt r sehuerw arozn Esura.nxep o nouAaakb ina ptlcriou B ahp pn,rgRa : eotluncieoled ae te io sliepon lfao nnhontf tb ituoeyinaoYmlhl ig.

h.ldtmpIeaRst t tnE ioyb)tldwclei lp h QhlhiRr.oy lo,lE nilacntnaePortAcoia bR)( S,lnvg B EparsradleuAIbdnceyEb (auEhtt o Sm tbe n, Dhi rhnu Naueoleiato_gcracoltbnathre w o ihoUh dpwrd iQa tePxekotoenotIaiBwto .ncftisRoamb a yuUW.

comments