dk.nsi.haiba.minipasconverter.dao.impl.MinipasDAOImpl.java Source code

Java tutorial

Introduction

Here is the source code for dk.nsi.haiba.minipasconverter.dao.impl.MinipasDAOImpl.java

Source

/**
 * The MIT License
 *
 * Original work sponsored and donated by National Board of e-Health (NSI), Denmark
 * (http://www.nsi.dk)
 *
 * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package dk.nsi.haiba.minipasconverter.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;

import dk.nsi.haiba.minipasconverter.dao.DAOException;
import dk.nsi.haiba.minipasconverter.dao.MinipasDAO;
import dk.nsi.haiba.minipasconverter.model.MinipasRowWithRecnum;
import dk.nsi.haiba.minipasconverter.model.MinipasTADM;
import dk.nsi.haiba.minipasconverter.model.MinipasTDIAG;
import dk.nsi.haiba.minipasconverter.model.MinipasTSKSUBE_OPR;

public class MinipasDAOImpl implements MinipasDAO {
    private static final Logger aLog = Logger.getLogger(MinipasDAOImpl.class);
    @Autowired
    @Qualifier("minipasJdbcTemplate")
    JdbcTemplate jdbc;

    @Value("${minipas.minipasstatustablename:T_MINIPAS_UGL_STATUS}")
    String minipasStatusTableName;

    @Value("${minipas.minipasprefix:MINIPAS_UGL.}")
    String minipasPrefix;

    Map<Integer, Collection<MinipasTSKSUBE_OPR>> aRecnumToSKSUBECollectionMap;
    String aCurrentSKSUBETableYear;
    Map<Integer, Collection<MinipasTSKSUBE_OPR>> aRecnumToSKSOPRCollectionMap;
    String aCurrentSKSOPRTableYear;
    Map<Integer, Collection<MinipasTDIAG>> aRecnumToDIAGCollectionMap;
    String aCurrentDIAGTableYear;

    // has to be large enough to include all diagnoses etc. for a single recnum, or they will be chopped up in the cache
    // and the import fails. largest amount seen is ube at a count of 644 in 2011
    @Value("${minipas.minipasdaocachesize:5000}")
    int cacheSize;

    @Override
    public void reset() {
        aRecnumToSKSUBECollectionMap = null;
        aRecnumToSKSOPRCollectionMap = null;
        aRecnumToDIAGCollectionMap = null;
    }

    @Override
    public Collection<MinipasTADM> getMinipasTADM(String year, long fromKRecnum, int batchSize) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.getMinipasTADM");
        String tableName = "T_ADM" + year;
        // maybe this works - needs to find out if order by executes on resultset or before (before is the intend)
        List<MinipasTADM> query = jdbc.query("SELECT * FROM " + minipasPrefix + tableName
                + " WHERE K_RECNUM > ? ORDER BY K_RECNUM FETCH FIRST " + batchSize + " ROWS ONLY",
                new RowMapper<MinipasTADM>() {
                    @Override
                    public MinipasTADM mapRow(ResultSet rs, int rowNum) throws SQLException {
                        MinipasTADM returnValue = new MinipasTADM();
                        returnValue.setC_adiag(rs.getString("C_ADIAG"));
                        returnValue.setC_afd(rs.getString("C_AFD"));
                        returnValue.setC_bopamt(rs.getString("C_BOPAMT"));
                        returnValue.setC_hafd(rs.getString("C_HAFD"));
                        returnValue.setC_henm(rs.getString("C_HENM"));
                        returnValue.setC_hsgh(rs.getString("C_HSGH"));
                        returnValue.setC_indm(rs.getString("C_INDM"));
                        returnValue.setC_kom(rs.getString("C_KOM"));
                        returnValue.setC_kontaars(rs.getString("C_KONTAARS"));
                        returnValue.setC_pattype(rs.getString("C_PATTYPE"));
                        returnValue.setC_sex(rs.getString("C_SEX"));
                        returnValue.setC_sgh(rs.getString("C_SGH"));
                        returnValue.setC_sghamt(rs.getString("C_SGHAMT"));
                        returnValue.setC_spec(rs.getString("C_SPEC"));
                        returnValue.setC_udm(rs.getString("C_UDM"));
                        returnValue.setD_hendto(rs.getTimestamp("D_HENDTO"));
                        returnValue.setD_inddto(rs.getTimestamp("D_INDDTO"));
                        returnValue.setD_uddto(rs.getTimestamp("D_UDDTO"));
                        returnValue.setIdnummer(rs.getString("IDNUMMER"));
                        returnValue.setK_recnum(rs.getInt("K_RECNUM"));
                        returnValue.setSkemaopdat(rs.getTimestamp("SKEMAOPDAT"));
                        returnValue.setSkemaopret(rs.getTimestamp("SKEMAOPRET"));
                        returnValue.setV_alder(rs.getInt("V_ALDER"));
                        returnValue.setV_behdage(rs.getInt("V_BEHDAGE"));
                        returnValue.setV_cpr(rs.getString("V_CPR"));
                        returnValue.setV_sengdage(rs.getInt("V_SENGDAGE"));
                        return returnValue;
                    }
                }, fromKRecnum);
        mon.stop();
        return query;
    }

    @Override
    public long lastReturnCodeElseNegativeOne() {
        try {
            String sql1 = "SELECT max(K_ID) FROM " + minipasPrefix + minipasStatusTableName;
            String sql2 = "SELECT V_RETURNCODE FROM " + minipasPrefix + minipasStatusTableName
                    + " WHERE K_ID = ? AND D_ENDDATETIME IS NOT NULL";

            long maxSyncId = jdbc.queryForLong(sql1);
            if (aLog.isTraceEnabled()) {
                aLog.trace("lastReturnCodeElseNegativeOne: maxSyncId=" + maxSyncId);
            }
            long returnCode = jdbc.queryForLong(sql2, maxSyncId);
            if (aLog.isTraceEnabled()) {
                aLog.trace("lastReturnCodeElseNegativeOne: returnCode=" + returnCode);
            }
            return returnCode;
        } catch (EmptyResultDataAccessException e) {
            // LPR is not ready for Import
        } catch (RuntimeException e) {
            throw new DAOException("Error fetching database status from " + minipasPrefix + minipasStatusTableName,
                    e);
        }
        return -1;
    }

    @Override
    public Collection<MinipasTSKSUBE_OPR> getMinipasSKSUBE_OPR(String year, int recnum) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.getMinipasSKSUBE_OPR");
        List<MinipasTSKSUBE_OPR> returnValue = new ArrayList<MinipasTSKSUBE_OPR>();
        returnValue.addAll(getMinipasSKSOPR(year, recnum));
        returnValue.addAll(getMinipasSKSUBE(year, recnum));
        mon.stop();
        return returnValue;
    }

    @Override
    public Collection<MinipasTDIAG> getMinipasDIAG(String year, int recnum) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.getMinipasDIAG");
        if (!year.equals(aCurrentDIAGTableYear)) {
            // new cache
            aCurrentDIAGTableYear = year;
            aRecnumToDIAGCollectionMap = null;
        }

        if (doBuildCache(aRecnumToDIAGCollectionMap, recnum)) {
            if (aLog.isTraceEnabled()) {
                aLog.trace("getMinipasDIAG: rebuilding cache");
            }
            // start or cache just got obsolete, refill it
            RowMapper<MinipasTDIAG> rowMapper = new MyMinipasTDIAGRowMapper();
            String tableName = "T_DIAG" + year;

            // evict
            aRecnumToDIAGCollectionMap = new HashMap<Integer, Collection<MinipasTDIAG>>();

            // start from the requested recnum, assuming to be asked in a recnum order
            buildCache(aRecnumToDIAGCollectionMap, rowMapper, tableName, recnum);
        }

        // most just goes here
        Collection<MinipasTDIAG> collection = aRecnumToDIAGCollectionMap.get(recnum);
        if (collection == null) {
            collection = new ArrayList<MinipasTDIAG>();
        }
        if (aLog.isTraceEnabled()) {
            aLog.trace("getMinipasDIAG: " + (collection.isEmpty() ? "none" : collection.size()) + " for " + recnum);
        }
        mon.stop();
        return collection;
    }

    private Collection<MinipasTSKSUBE_OPR> getMinipasSKSUBE(String year, int recnum) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.getMinipasSKSUBE");
        if (!year.equals(aCurrentSKSUBETableYear)) {
            // new cache
            aCurrentSKSUBETableYear = year;
            aRecnumToSKSUBECollectionMap = null;
        }

        if (doBuildCache(aRecnumToSKSUBECollectionMap, recnum)) {
            if (aLog.isTraceEnabled()) {
                aLog.trace("getMinipasSKSUBE: rebuilding cache");
            }
            // start or cache just got obsolete, refill it
            RowMapper<MinipasTSKSUBE_OPR> rowMapper = new MyMinipasTSKSUBE_OPRRowMapper("und");
            String tableName = "T_SKSUBE" + year;

            // evict
            aRecnumToSKSUBECollectionMap = new HashMap<Integer, Collection<MinipasTSKSUBE_OPR>>();

            // start from the requested recnum, assuming to be asked in a recnum order
            buildCache(aRecnumToSKSUBECollectionMap, rowMapper, tableName, recnum);
        }

        // most just goes here
        Collection<MinipasTSKSUBE_OPR> collection = aRecnumToSKSUBECollectionMap.get(recnum);
        if (collection == null) {
            collection = new ArrayList<MinipasTSKSUBE_OPR>();
        }
        if (aLog.isTraceEnabled()) {
            aLog.trace(
                    "getMinipasSKSUBE: " + (collection.isEmpty() ? "none" : collection.size()) + " for " + recnum);
        }
        mon.stop();
        return collection;
    }

    private Collection<MinipasTSKSUBE_OPR> getMinipasSKSOPR(String year, int recnum) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.getMinipasSKSOPR");
        if (!year.equals(aCurrentSKSOPRTableYear)) {
            // new cache
            aCurrentSKSOPRTableYear = year;
            aRecnumToSKSOPRCollectionMap = null;
        }

        if (doBuildCache(aRecnumToSKSOPRCollectionMap, recnum)) {
            if (aLog.isTraceEnabled()) {
                aLog.trace("getMinipasSKSOPR: rebuilding cache");
            }

            // start or cache just got obsolete, refill it
            RowMapper<MinipasTSKSUBE_OPR> rowMapper = new MyMinipasTSKSUBE_OPRRowMapper("opr");
            String tableName = "T_SKSOPR" + year;

            // evict
            aRecnumToSKSOPRCollectionMap = new HashMap<Integer, Collection<MinipasTSKSUBE_OPR>>();

            // start from the requested recnum, assuming to be asked in a recnum order
            buildCache(aRecnumToSKSOPRCollectionMap, rowMapper, tableName, recnum);
        }

        // most just goes here
        Collection<MinipasTSKSUBE_OPR> collection = aRecnumToSKSOPRCollectionMap.get(recnum);
        if (collection == null) {
            collection = new ArrayList<MinipasTSKSUBE_OPR>();
        }
        if (aLog.isTraceEnabled()) {
            aLog.trace(
                    "getMinipasSKSOPR: " + (collection.isEmpty() ? "none" : collection.size()) + " for " + recnum);
        }
        mon.stop();
        return collection;
    }

    private <T extends MinipasRowWithRecnum> boolean doBuildCache(Map<Integer, Collection<T>> map, int recnum) {
        // rebuild if we have no map, if the recnum is not in the map or if the recnum is not inside the range of the
        // recnums last fetched
        boolean returnValue = map == null || (!map.containsKey(recnum) && recnumNotInKeyRange(map, recnum));
        return returnValue;
    }

    private <T extends MinipasRowWithRecnum> boolean recnumNotInKeyRange(Map<Integer, Collection<T>> map,
            int recnum) {
        boolean returnValue = false;
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        Set<Integer> keySet = map.keySet();
        for (Integer key : keySet) {
            min = Math.min(min, key);
            max = Math.max(max, key);
        }
        returnValue = (recnum < min) || (max < recnum);
        if (aLog.isTraceEnabled()) {
            aLog.trace(
                    "recnumNotInKeyRange: " + returnValue + ", min=" + min + ", max=" + max + ", recnum=" + recnum);
        }
        return returnValue;
    }

    // All this assumes that we are asked in an recnum ordered way. if lowest recnums are here first, then we request
    // from this recnum and forward a batch size. when this batch is done, we get a new batch and so on
    public <T extends MinipasRowWithRecnum> void buildCache(Map<Integer, Collection<T>> destination,
            RowMapper<T> rowMapper, String tableName, int currentRecnum) {
        Monitor mon = MonitorFactory.start("MinipasDAOImpl.buildCache");
        List<T> query = jdbc.query("SELECT * FROM " + minipasPrefix + tableName
                + " WHERE V_RECNUM >= ? ORDER BY V_RECNUM FETCH FIRST " + cacheSize + " ROWS ONLY", rowMapper,
                currentRecnum);

        // be sure to tell that we have already been here for this recnum, even if there is no data in the loop
        destination.put(currentRecnum, new ArrayList<T>());
        for (T t : query) {
            // store in cache
            Collection<T> collection = destination.get(t.getV_recnum());
            if (collection == null) {
                collection = new ArrayList<T>();
                destination.put(t.getV_recnum(), collection);
            }
            collection.add(t);
        }

        if (query.size() == cacheSize) {
            if (destination.size() == 1) {
                // fatal error, we can never retrieve all data for this recnum
                throw new RuntimeException(
                        "cache size of " + cacheSize + " is not enough to contain data for recnum " + currentRecnum
                                + " from table " + tableName);
            }
            // drop the collection with the largest recnum as this may have been cut short (by the fetch limit)
            Integer max_recnum = Collections.max(destination.keySet());
            destination.remove(max_recnum);
        }

        mon.stop();
    }

    private final class MyMinipasTSKSUBE_OPRRowMapper implements RowMapper<MinipasTSKSUBE_OPR> {
        private final String aType;

        private MyMinipasTSKSUBE_OPRRowMapper(String type) {
            aType = type;
        }

        @Override
        public MinipasTSKSUBE_OPR mapRow(ResultSet rs, int rowNum) throws SQLException {
            MinipasTSKSUBE_OPR returnValue = new MinipasTSKSUBE_OPR();
            returnValue.setC_oafd(rs.getString("C_OAFD"));
            returnValue.setC_opr(rs.getString("C_OPR"));
            returnValue.setC_oprart(rs.getString("C_OPRART"));
            returnValue.setC_osgh(rs.getString("C_OSGH"));
            returnValue.setC_tilopr(rs.getString("C_TILOPR"));
            returnValue.setD_odto(rs.getTimestamp("D_ODTO"));
            returnValue.setIdnummer(rs.getString("IDNUMMER"));
            returnValue.setIndberetningsdato(rs.getTimestamp("INDBERETNINGSDATO"));
            returnValue.setV_recnum(rs.getInt("V_RECNUM"));
            returnValue.setType(aType);
            return returnValue;
        }
    }

    private final class MyMinipasTDIAGRowMapper implements RowMapper<MinipasTDIAG> {
        @Override
        public MinipasTDIAG mapRow(ResultSet rs, int rowNum) throws SQLException {
            MinipasTDIAG returnValue = new MinipasTDIAG();
            returnValue.setC_diag(rs.getString("C_DIAG"));
            returnValue.setC_diagtype(rs.getString("C_DIAGTYPE"));
            returnValue.setC_tildiag(rs.getString("C_TILDIAG"));
            returnValue.setIdnummer(rs.getString("IDNUMMER"));
            returnValue.setIndberetningsdato(rs.getTimestamp("INDBERETNINGSDATO"));
            returnValue.setV_recnum(rs.getInt("V_RECNUM"));
            return returnValue;
        }
    }
}