class JdbcSessionRepository implements
        SessionRepository<JdbcSessionRepository.JdbcSession> {

    @Override
    public void save(JdbcSession session) {
        if (session.isNew()) {
            try (final Connection connection = dataSource.getConnection()) {
                connection.setAutoCommit(false);

                PreparedStatement stmt = connection.prepareStatement(this.createSessionQuery);
                stmt.setString(1, session.getId());
                stmt.setLong(2, session.getCreationTime());
                stmt.setLong(3, session.getLastAccessedTime());
                stmt.setInt(4, session.getMaxInactiveInterval());
                stmt.setLong(5, session.getExpiryTime());
                stmt.setString(6, session.getPrincipalName());
                int count = stmt.executeUpdate();
                if (!session.getAttributeNames().isEmpty()) {
                    final List<String> attributeNames = new ArrayList<>(session.getAttributeNames());
                    try (PreparedStatement psAtt = connection.prepareStatement(this.createSessionAttributeQuery)) {
                        for (String attributeName : attributeNames) {
                            psAtt.setString(1, session.getId());
                            psAtt.setString(2, attributeName);
                            serialize(psAtt, 3, session.getAttribute(attributeName));
                            psAtt.addBatch();
                        }
                        psAtt.executeBatch();
                    }
                }
                connection.commit();

                if (count != 1) {
                    logger.error("Failed to insert session: {}", session.getId());
                }
            } catch (SQLException e) {
                logger.error("SqlException:", e);
            }
        } else {
            try (final Connection connection = dataSource.getConnection()) {

                connection.setAutoCommit(false);
                PreparedStatement stmt = connection.prepareStatement(this.updateSessionQuery);
                stmt.setString(1, session.getId());
                stmt.setLong(2, session.getLastAccessedTime());
                stmt.setInt(3, session.getMaxInactiveInterval());
                stmt.setLong(4, session.getExpiryTime());
                stmt.setString(5, session.getPrincipalName());
                stmt.setString(6, session.getId());
                int count = stmt.executeUpdate();

                Map<String, Object> delta = session.getDelta();
                if (!delta.isEmpty()) {
                    for (final Map.Entry<String, Object> entry : delta.entrySet()) {
                        if (entry.getValue() == null) {
                            try (PreparedStatement psAtt = connection.prepareStatement(this.deleteSessionAttributeQuery)) {
                                psAtt.setString(1, session.getId());
                                psAtt.setString(2, entry.getKey());
                                psAtt.executeUpdate();
                            }
                        } else {
                            int updatedCount = 0;
                            try (PreparedStatement psAtt = connection.prepareStatement(this.updateSessionAttributeQuery)) {
                                serialize(psAtt, 1, entry.getValue());
                                psAtt.setString(2, session.getId());
                                psAtt.setString(3, entry.getKey());
                                updatedCount = psAtt.executeUpdate();
                            }
                            if (updatedCount == 0) {
                                try (PreparedStatement psAtt = connection.prepareStatement(this.createSessionAttributeQuery)) {
                                    psAtt.setString(1, session.getId());
                                    psAtt.setString(2, entry.getKey());
                                    serialize(psAtt, 3, entry.getValue());

                                    psAtt.executeUpdate();
                                }
                            }

                        }
                    }
                }
                connection.commit();
            } catch (SQLException e) {
                logger.error("SqlException:", e);
            }
        }
        session.clearChangeFlags();
    }

    public  JdbcSessionRepository(DataSource dataSource);

}