/*
*
* Copyright 2013 Entando S.r.l. (http://www.entando.com) All rights reserved.
*
* This file is part of Entando software.
* Entando is a free software;
* You can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) as published by the Free Software Foundation; version 2.
*
* See the file License for the specific language governing permissions
* and limitations under the License
*
*
*
* Copyright 2013 Entando S.r.l. (http://www.entando.com) All rights reserved.
*
*/
package com.agiletec.plugins.jpnewsletter.aps.system.services.newsletter;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.agiletec.aps.system.common.AbstractDAO;
import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.plugins.jpnewsletter.aps.system.services.newsletter.model.ContentReport;
import com.agiletec.plugins.jpnewsletter.aps.system.services.newsletter.model.NewsletterContentReportVO;
import com.agiletec.plugins.jpnewsletter.aps.system.services.newsletter.model.NewsletterReport;
import com.agiletec.plugins.jpnewsletter.aps.system.services.newsletter.model.Subscriber;
/**
* Data Access Object per gli oggetti contenenti le informazioni di spedizione newsletter.
* @author E.Mezzano
*/
public class NewsletterDAO extends AbstractDAO implements INewsletterDAO {
@Override
public void addContentToQueue(String contentId) {
Connection conn = null;
PreparedStatement stat = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
stat = conn.prepareStatement(ADD_CONTENT_TO_QUEUE);
stat.setString(1, contentId);
stat.executeUpdate();
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error putting content into queue", "addContentToQueue");
} finally {
closeDaoResources(null, stat, conn);
}
}
@Override
public void deleteContentFromQueue(String contentId) {
Connection conn = null;
PreparedStatement stat = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
stat = conn.prepareStatement(DELETE_CONTENT_FROM_QUEUE);
stat.setString(1, contentId);
stat.executeUpdate();
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error removing content from queue", "deleteContentFromQueue");
} finally {
closeDaoResources(null, stat, conn);
}
}
@Override
public List<String> loadContentQueue() {
List<String> queue = new ArrayList<String>();
Connection conn = null;
Statement stat = null;
ResultSet res = null;
try {
conn = this.getConnection();
stat = conn.createStatement();
res = stat.executeQuery(LOAD_CONTENT_QUEUE);
while (res.next()) {
queue.add(res.getString(1));
}
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error loading content queue", "loadContentQueue");
} finally {
closeDaoResources(res, stat, conn);
}
return queue;
}
@Override
public void cleanContentQueue(List<String> queue) {
Connection conn = null;
PreparedStatement stat = null;
if (queue != null && queue.size() > 0) {
try {
conn = this.getConnection();
conn.setAutoCommit(false);
StringBuffer query = new StringBuffer(CLEAN_CONTENT_QUEUE_PREFIX);
int size = queue.size();
query.append(" ?");
for (int i=1; i<size; i++) {
query.append(", ?");
}
query.append(" ) ");
stat = conn.prepareStatement(query.toString());
int index = 1;
for (String id : queue) {
stat.setString(index++, id);
}
stat.executeUpdate();
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error cleaning content queue", "cleanContentQueue");
} finally {
closeDaoResources(null, stat, conn);
}
}
}
@Override
public void addNewsletterReport(NewsletterReport newsletterReport) {
Connection conn = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
this.addNewsletterReport(newsletterReport, conn);
this.addContentReports(newsletterReport, conn);
this.addContentRecipients(newsletterReport, conn);
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
this.processDaoException(t, "Error adding newsletter report", "addNewsletterReport");
} finally {
closeConnection(conn);
}
}
@Override
public NewsletterContentReportVO loadContentReport(String contentId) {
NewsletterContentReportVO contentReport = null;
Connection conn = null;
try {
conn = this.getConnection();
contentReport = this.getContentReport(contentId, conn);
if (contentReport != null) {
Map<String, String> recipients = this.getContentRecipients(contentReport.getId(), conn);
contentReport.setRecipients(recipients);
}
} catch (Throwable t) {
this.processDaoException(t, "Error loading content report", "loadContentReport");
} finally {
closeConnection(conn);
}
return contentReport;
}
public List<String> loadSentContentIds() {
List<String> sentContentIds = new ArrayList<String>();
Connection conn = null;
Statement stat = null;
ResultSet res = null;
try {
conn = this.getConnection();
stat = conn.createStatement();
res = stat.executeQuery(LOAD_SENT_CONTENTS);
while (res.next()) {
sentContentIds.add(res.getString(1));
}
} catch (Throwable t) {
processDaoException(t, "Error loading sent contents ids", "loadSentContentIds");
} finally {
closeDaoResources(res, stat, conn);
}
return sentContentIds;
}
@Override
public boolean existsContentReport(String contentId) {
boolean sent = false;
Connection conn = null;
PreparedStatement stat = null;
ResultSet res = null;
try {
conn = this.getConnection();
stat = conn.prepareStatement(EXISTS_CONTENT_REPORT);
stat.setString(1, contentId);
res = stat.executeQuery();
if (res.next()) {
sent = true;
}
} catch (Throwable t) {
processDaoException(t, "Error verifying content report existence", "existsContentReport");
} finally {
closeDaoResources(res, stat, conn);
}
return sent;
}
private void addNewsletterReport(NewsletterReport newsletterReport, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(ADD_NEWSLETTER_REPORT);
stat.setInt(1, newsletterReport.getId());
stat.setTimestamp(2, new Timestamp(newsletterReport.getSendDate().getTime()));
stat.setString(3, newsletterReport.getSubject());
stat.executeUpdate();
} catch (Throwable t) {
processDaoException(t, "Error adding report for sent newsletter", "addNewsletterReport");
} finally {
closeDaoResources(null, stat);
}
}
private void addContentReports(NewsletterReport newsletterReport, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(ADD_CONTENT_REPORT);
int reportId = newsletterReport.getId();
for (ContentReport contentReport : newsletterReport.getContentReports().values()) {
stat.setInt(1, contentReport.getId());
stat.setInt(2, reportId);
stat.setString(3, contentReport.getContentId());
stat.setString(4, contentReport.getTextBody());
stat.setString(5, contentReport.getHtmlBody());
stat.addBatch();
stat.clearParameters();
}
stat.executeBatch();
} catch (BatchUpdateException e) {
this.processDaoException(e.getNextException(), "Error adding contents for sent newsletter",
"addContentReports");
} catch (Throwable t) {
this.processDaoException(t, "Error adding contents for sent newsletter", "addContentReports");
} finally {
closeDaoResources(null, stat);
}
}
private void addContentRecipients(NewsletterReport newsletterReport, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(ADD_CONTENT_RECIPIENT);
for (ContentReport contentReport : newsletterReport.getContentReports().values()) {
int id = contentReport.getId();
contentReport.getRecipients();
for (Entry<String, String> recipient : contentReport.getRecipients().entrySet()) {
stat.setInt(1, id);
stat.setString(2, recipient.getKey());
stat.setString(3, recipient.getValue());
stat.addBatch();
stat.clearParameters();
}
}
stat.executeBatch();
} catch (BatchUpdateException e) {
this.processDaoException(e.getNextException(), "Error adding recipients for sent newsletter",
"addContentRecipients");
} catch (Throwable t) {
this.processDaoException(t, "Error adding recipients for sent newsletter", "addContentRecipients");
} finally {
closeDaoResources(null, stat);
}
}
private NewsletterContentReportVO getContentReport(String contentId, Connection conn) {
NewsletterContentReportVO contentReport = null;
PreparedStatement stat = null;
ResultSet res = null;
try {
stat = conn.prepareStatement(LOAD_CONTENT_REPORT);
stat.setString(1, contentId);
res = stat.executeQuery();
if (res.next()) {
contentReport = this.createContentReportFromRecord(res);
}
} catch (Throwable t) {
this.processDaoException(t, "Error loading content report", "getContentReport");
} finally {
closeDaoResources(res, stat);
}
return contentReport;
}
private Map<String, String> getContentRecipients(int contentReportId, Connection conn) {
Map<String, String> recipients = new HashMap<String, String>();
PreparedStatement stat = null;
ResultSet res = null;
try {
stat = conn.prepareStatement(LOAD_CONTENT_RECIPIENTS);
stat.setInt(1, contentReportId);
res = stat.executeQuery();
while (res.next()) {
recipients.put(res.getString(1), res.getString(2));
}
} catch (Throwable t) {
this.processDaoException(t, "Error loading content recipients", "getContentRecipients");
} finally {
closeDaoResources(res, stat);
}
return recipients;
}
private NewsletterContentReportVO createContentReportFromRecord(ResultSet res) throws SQLException {
NewsletterContentReportVO contentReport = new NewsletterContentReportVO();
contentReport.setId(res.getInt(1));
contentReport.setNewsletterId(res.getInt(2));
contentReport.setContentId(res.getString(3));
contentReport.setTextBody(res.getString(4));
contentReport.setHtmlBody(res.getString(5));
contentReport.setSendDate(res.getTimestamp(6));
contentReport.setSubject(res.getString(7));
return contentReport;
}
@Override
public List<Subscriber> loadSubscribers() throws ApsSystemException {
List<Subscriber> subscribers = new ArrayList<Subscriber>();
Connection conn = null;
PreparedStatement stat = null;
ResultSet res = null;
try {
conn = this.getConnection();
stat = conn.prepareStatement(LOAD_SUBSCRIBERS);
res = stat.executeQuery();
while(res.next()){
Subscriber subscriber = this.loadSubscriberFromResulSet(res);
subscribers.add(subscriber);
}
} catch (Throwable t) {
this.processDaoException(t, "Errore durante il caricamento della lista degli indirizzi e-mail", "loadSubscribers");
} finally {
this.closeDaoResources(res,stat,conn);
}
return subscribers;
}
@Override
public List<Subscriber> searchSubscribers(String mailAddress, Boolean active) throws ApsSystemException {
List<Subscriber> subscribers = new ArrayList<Subscriber>();
Connection conn = null;
PreparedStatement stat = null;
ResultSet res = null;
try {
conn = this.getConnection();
stat = this.buildStatement(mailAddress, active, conn);
res = stat.executeQuery();
this.flowResult(subscribers, mailAddress, res);
} catch (Throwable t) {
this.processDaoException(t, "Errore durante il caricamento della lista degli indirizzi e-mail", "searchSubscribers");
} finally {
this.closeDaoResources(res,stat,conn);
}
return subscribers;
}
private PreparedStatement buildStatement(String mailAddress, Boolean active, Connection conn) {
String query = this.createQueryString(mailAddress, active);
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(query);
if (null != active) {
int intActive = active.booleanValue() ? 1 : 0;
stat.setInt(1, intActive);
}
} catch (Throwable t) {
processDaoException(t, "Error while creating the statement", "buildStatement");
}
return stat;
}
private String createQueryString(String mailAddress, Boolean active) {
boolean hasAppendWhereClause = false;
StringBuffer query = new StringBuffer("SELECT * FROM jpnewsletter_subscribers ");
if (null != active) {
hasAppendWhereClause = this.verifyWhereClauseAppend(query, hasAppendWhereClause);
query.append(" active = ? ");
}
query.append(" ORDER BY jpnewsletter_subscribers.mailaddress ");
return query.toString();
}
protected void flowResult(List<Subscriber> subscribers, String mailAddress, ResultSet res) throws SQLException {
if (null != mailAddress && mailAddress.trim().length() > 0) {
mailAddress = mailAddress.trim().toLowerCase();
while (res.next()) {
String id = res.getString("mailaddress");
if (id.toLowerCase().indexOf(mailAddress) != -1) {
Subscriber subscriber = this.loadSubscriberFromResulSet(res);
subscribers.add(subscriber);
}
}
} else {
while (res.next()) {
Subscriber subscriber = this.loadSubscriberFromResulSet(res);
subscribers.add(subscriber);
}
}
}
private boolean verifyWhereClauseAppend(StringBuffer query, boolean hasAppendWhereClause) {
if (hasAppendWhereClause) {
query.append("AND ");
} else {
query.append("WHERE ");
hasAppendWhereClause = true;
}
return hasAppendWhereClause;
}
@Override
public Subscriber loadSubscriber(String mailAddress) throws ApsSystemException {
Subscriber subscriber = null;
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
try {
connection = this.getConnection();
statement = connection.prepareStatement(LOAD_BY_MAIL);
statement.setString(1, mailAddress);
res = statement.executeQuery();
if (res.next()) {
subscriber = this.loadSubscriberFromResulSet(res);
}
} catch (Throwable t) {
this.processDaoException(t, "Errore durante il caricamento della sottoscrizione", "loadSubscriber");
} finally {
this.closeDaoResources(res, statement, connection);
}
return subscriber;
}
private Subscriber loadSubscriberFromResulSet(ResultSet res) throws SQLException {
Subscriber subscriber = new Subscriber();
String mailAddress = res.getString("mailaddress");
Date subscriptionDate = res.getDate("subscription_date");
subscriber.setMailAddress(mailAddress);
subscriber.setSubscriptionDate(subscriptionDate);
int active = res.getInt("active");
subscriber.setActive(active==1);
return subscriber;
}
@Override
public void addSubscriber(Subscriber subscriber, String token) throws ApsSystemException {
Connection conn = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
this.addSubscriber(subscriber, conn);
if (token!=null) {
this.removeTokenByAddress(subscriber.getMailAddress(), conn);
this.addToken(subscriber, token, conn);
}
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
this.processDaoException(t, "Errore durante l'aggiunta di una sottoscrizione", "addSubscriber");
} finally {
this.closeConnection(conn);
}
}
private void addSubscriber(Subscriber subscriber, Connection conn) throws ApsSystemException {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(ADD_SUBSCRIBER);
stat.setString(1, subscriber.getMailAddress());
Date subscriptionDate = subscriber.getSubscriptionDate();
stat.setDate(2, subscriptionDate==null ? null : new java.sql.Date(subscriptionDate.getTime()));
boolean active = subscriber.isActive();
stat.setInt(3, active ? 1 : 0);
stat.executeUpdate();
} catch (Throwable t) {
this.processDaoException(t, "Errore durante l'aggiunta di una sottoscrizione", "addSubscriber");
} finally {
this.closeDaoResources(null, stat);
}
}
@Override
public void updateSubscriber(Subscriber subscriber, String token) throws ApsSystemException {
Connection conn = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
this.updateSubscriber(subscriber, conn);
if (token != null) {
this.removeTokenByAddress(subscriber.getMailAddress(), conn);
this.addToken(subscriber, token, conn);
}
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
this.processDaoException(t, "Errore durante l'aggiornamento di una sottoscrizione", "updateSubscriber");
} finally {
this.closeConnection(conn);
}
}
private void updateSubscriber(Subscriber subscriber, Connection conn) throws ApsSystemException {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(UPDATE_SUBSCRIBER);
Date subscriptionDate = subscriber.getSubscriptionDate();
stat.setDate(1, subscriptionDate==null ? null : new java.sql.Date(subscriptionDate.getTime()));
boolean active = subscriber.isActive();
stat.setInt(2, active ? 1 : 0);
stat.setString(3, subscriber.getMailAddress());
stat.executeUpdate();
} catch (Throwable t) {
this.processDaoException(t, "Errore durante l'aggiornamento di una sottoscrizione", "updateSubscriber");
} finally {
this.closeDaoResources(null, stat);
}
}
private void addToken(Subscriber subscriber, String token, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(ADD_TOKEN);
stat.setString(1, subscriber.getMailAddress());
stat.setString(2, token);
stat.setTimestamp(3, new Timestamp(subscriber.getSubscriptionDate().getTime()));
stat.executeUpdate();
} catch (Throwable t) {
processDaoException(t, "Error adding token for subscription activation", "addToken");
} finally {
closeDaoResources(null, stat);
}
}
@Override
public void deleteSubscriber(String mailAddress) throws ApsSystemException {
Connection conn = null;
PreparedStatement stat = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
stat = conn.prepareStatement(DELETE_SUBSCRIBER);
stat.setString(1, mailAddress);
stat.executeUpdate();
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
this.processDaoException(t, "Errore durante la cancellazione di una sottoscrizione", "deleteSubscriber");
} finally {
this.closeDaoResources(null,stat,conn);
}
}
@Override
public void activateSubscriber(String mailAddress) throws ApsSystemException {
Connection conn = null;
PreparedStatement stat = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
stat = conn.prepareStatement(ACTIVATE_SUBSCRIBER);
stat.setString(1, mailAddress);
stat.executeUpdate();
this.removeTokenByAddress(mailAddress, conn);
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
this.processDaoException(t, "Errore durante l'attivazione di una sottoscrizione", "updateSubscriber");
} finally {
this.closeDaoResources(null, stat, conn);
}
}
@Override
public String getAddressFromToken(String token) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet res = null;
String username = null;
try {
conn = this.getConnection();
stat = conn.prepareStatement(GET_ADDRESS_FROM_TOKEN);
stat.setString(1,token);
res = stat.executeQuery();
if (res.next()) {
username = res.getString(1);
}
} catch (Throwable t) {
processDaoException(t, "Error getting address from token", "getAddressFromToken");
} finally {
closeDaoResources(res, stat, conn);
}
return username;
}
@Override
public void removeToken(String token) {
Connection conn = null;
PreparedStatement stat = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
stat = conn.prepareStatement(DELETE_TOKEN);
stat.setString(1, token);
stat.execute();
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error removing Token", "removeToken");
} finally {
closeDaoResources(null, stat, conn);
}
}
@Override
public void cleanOldSubscribers(Date date) {
Connection conn = null;
try {
conn = this.getConnection();
conn.setAutoCommit(false);
this.clearOldSubscribers(date, conn);
this.clearOldTokens(date, conn);
conn.commit();
} catch (Throwable t) {
this.executeRollback(conn);
processDaoException(t, "Error in clear old subscribers", "cleanOldSubscribers");
} finally {
closeConnection(conn);
}
}
private void clearOldSubscribers(Date date, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(DELETE_OLD_SUBSCRIBERS);
stat.setTimestamp(1, new Timestamp(date.getTime()));
stat.executeUpdate();
} catch (Throwable t) {
processDaoException(t, "Error in clear old tokens", "clearOldSubscribers");
} finally {
closeDaoResources(null, stat);
}
}
private void clearOldTokens(Date date, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(DELETE_OLD_TOKENS);
stat.setTimestamp(1, new Timestamp(date.getTime()));
stat.executeUpdate();
} catch (Throwable t) {
processDaoException(t, "Error in clear old tokens", "clearOldSubscribers");
} finally {
closeDaoResources(null, stat);
}
}
private void removeTokenByAddress(String mailAddress, Connection conn) {
PreparedStatement stat = null;
try {
stat = conn.prepareStatement(DELETE_ADDRESS_TOKENS);
stat.setString(1, mailAddress);
stat.executeUpdate();
conn.commit();
} catch (Throwable t) {
processDaoException(t, "Error in clear tokens by address", "clearTokenByAddress");
} finally {
closeDaoResources(null, stat);
}
}
private final String ADD_CONTENT_TO_QUEUE = "INSERT INTO jpnewsletter_contentqueue ( contentid ) VALUES ( ? ) ";
private final String DELETE_CONTENT_FROM_QUEUE = "DELETE FROM jpnewsletter_contentqueue WHERE contentid = ? ";
private final String LOAD_CONTENT_QUEUE = "SELECT contentid FROM jpnewsletter_contentqueue ";
private final String CLEAN_CONTENT_QUEUE_PREFIX = "DELETE FROM jpnewsletter_contentqueue WHERE contentid IN ( ";
private final String ADD_NEWSLETTER_REPORT =
"INSERT INTO jpnewsletter_newsletterreport ( id, date, subject ) VALUES ( ?, ?, ? )";
private final String ADD_CONTENT_REPORT =
"INSERT INTO jpnewsletter_contentreport ( id, newsletterid, contentid, textbody, htmlbody ) VALUES ( ?, ?, ?, ?, ? )";
private final String ADD_CONTENT_RECIPIENT =
"INSERT INTO jpnewsletter_recipient ( contentreportid, username, mailaddress ) VALUES ( ?, ?, ? )";
private final String LOAD_CONTENT_REPORT =
"SELECT c.id, c.newsletterid, c.contentid, c.textbody, c.htmlbody, r.date, r.subject " +
"FROM jpnewsletter_contentreport AS c JOIN jpnewsletter_newsletterreport r ON c.newsletterid = r.id " +
"WHERE contentid = ? ORDER BY r.date DESC ";
private final String LOAD_CONTENT_RECIPIENTS =
"SELECT username, mailaddress FROM jpnewsletter_recipient WHERE contentreportid = ? ";
private final String LOAD_SENT_CONTENTS = "SELECT DISTINCT contentid FROM jpnewsletter_contentreport ";
private final String EXISTS_CONTENT_REPORT =
"SELECT contentid FROM jpnewsletter_contentreport WHERE contentid = ?";
private static final String ADD_SUBSCRIBER =
"INSERT INTO jpnewsletter_subscribers (mailaddress, subscription_date, active) VALUES (?, ?, ?)";
private static final String UPDATE_SUBSCRIBER =
"UPDATE jpnewsletter_subscribers SET subscription_date = ?, active = ? WHERE mailaddress = ?";
private static final String DELETE_SUBSCRIBER =
"DELETE FROM jpnewsletter_subscribers WHERE mailaddress = ?";
private static final String LOAD_SUBSCRIBERS =
"SELECT * FROM jpnewsletter_subscribers ORDER BY mailaddress";
private static final String ACTIVATE_SUBSCRIBER =
"UPDATE jpnewsletter_subscribers SET active = 1 WHERE mailaddress = ?";
private static final String LOAD_BY_MAIL =
"SELECT * FROM jpnewsletter_subscribers WHERE mailaddress = ?";
private static final String ADD_TOKEN =
"INSERT INTO jpnewsletter_subscribertokens ( mailaddress, token, regtime ) VALUES ( ?, ?, ? )";
private static final String GET_ADDRESS_FROM_TOKEN =
"SELECT mailaddress FROM jpnewsletter_subscribertokens WHERE token = ?";
private static final String DELETE_TOKEN =
"DELETE FROM jpnewsletter_subscribertokens WHERE token = ?";
private static final String DELETE_OLD_SUBSCRIBERS =
"DELETE FROM jpnewsletter_subscribers WHERE subscription_date < ? AND active = 0";
private static final String DELETE_OLD_TOKENS =
"DELETE FROM jpnewsletter_subscribertokens WHERE regtime < ?";
private static final String DELETE_ADDRESS_TOKENS =
"DELETE FROM jpnewsletter_subscribertokens WHERE mailaddress = ?";
}