Это вымышленная история, и все совпадения случайны.
Наконец-то команда разработки компании Unknown Ltd. выпустила релиз вовремя. Руководитель отдела разработки Эндрю, системный архитектор Юг и простой рядовой разработчик Боб собрались на планирование.
В предстоящий квартал они решили взять больше задач из тех. долга, так как практически весь предыдущий был посвящен исправлению багов и выполнению срочных доработок под конкретных клиентов.
Все уселись поудобнее и начали обсуждать предстоящий план. Боб сразу обратил внимание на задачу по переработке генерации документов. Суть задачи заключалась в том, что генерируемые документы состоят из схемы и настроек генерации и непосредственно самого документа. При сохранении в БД документ сериализуется в XML, конвертируется в поток байт и сжимается, а потом все стандартненько — помещается в колонку типа BLOB. Когда нужно отобразить в системе или выгрузить документ, то все повторяется с точностью да наоборот, и вуаля, документик красуется на экране клиента. Вот так, все просто. Но, как известно, дьявол кроется в мелочах. Чтобы заново сгенерировать документ, если необходимо изменить настройки, то приходится целиком загружать весь документ из БД, хотя содержимое его совершенно не нужно. Ай-я-яй. Обсудили задачу. Пришли к выводу, что Бобу предстоит сделать следующее:
public class MigratorV1 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
public void migrate() throws Exception {
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data");
while (oldIdResult.next()) {
long id = oldIdResult.getLong(1);
selectOldContent.setLong(1, id);
ResultSet oldContentResult = selectOldContent.executeQuery();
oldContentResult.next();
Blob oldContent = oldContentResult.getBlob(1);
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
XMLStreamWriter newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter);
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
XMLStreamWriter newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8");
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
String newScheme = newSchemeWriter.toString();
byte[] newData = newDataOutput.toByteArray();
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
}
public class MigratorV2 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
public void migrate() throws Exception {
try (
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data");
){
while (oldIdResult.next()) {
long id = oldIdResult.getLong(1);
selectOldContent.setLong(1, id);
try (ResultSet oldContentResult = selectOldContent.executeQuery()) {
oldContentResult.next();
String newScheme;
byte[] newData;
Blob oldContent = null;
try {
oldContent = oldContentResult.getBlob(1);
try (
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
){
XMLStreamWriter newSchemeXMLWriter = null;
XMLStreamWriter newDataXMLWriter = null;
try {
newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter);
newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8");
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
} finally {
if (newSchemeXMLWriter != null) {
try {
newSchemeXMLWriter.close();
} catch (XMLStreamException e) {}
}
if (newDataXMLWriter != null) {
try {
newDataXMLWriter.close();
} catch (XMLStreamException e) {}
}
}
newScheme = newSchemeWriter.toString();
newData = newDataOutput.toByteArray();
}
} finally {
if (oldContent != null) {
try {
oldContent.free();
} catch (SQLException e) {}
}
}
try (
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
){
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
}
}
}
}
public class MigratorV3 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
@RequiredArgsConstructor
private static class NewData {
final String scheme;
final byte[] data;
}
private List<Long> loadIds() throws Exception {
List<Long> ids = new ArrayList<>();
try (ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data")) {
while (oldIdResult.next()) {
ids.add(oldIdResult.getLong(1));
}
}
return ids;
}
private Blob loadOldContent(PreparedStatement selectOldContent, long id) throws Exception {
selectOldContent.setLong(1, id);
try (ResultSet oldContentResult = selectOldContent.executeQuery()) {
oldContentResult.next();
return oldContentResult.getBlob(1);
}
}
private void oldContentToNewData(Reader oldContentReader, StringWriter newSchemeWriter, GZIPOutputStream newZippedDataOutput) throws Exception {
XMLStreamWriter newSchemeXMLWriter = null;
XMLStreamWriter newDataXMLWriter = null;
try {
newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter);
newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8");
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
} finally {
if (newSchemeXMLWriter != null) {
try {
newSchemeXMLWriter.close();
} catch (XMLStreamException e) {}
}
if (newDataXMLWriter != null) {
try {
newDataXMLWriter.close();
} catch (XMLStreamException e) {}
}
}
}
private NewData generateNewDataFromOldContent(PreparedStatement selectOldContent, long id) throws Exception {
Blob oldContent = null;
try {
oldContent = loadOldContent(selectOldContent, id);
try (
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
){
oldContentToNewData(oldContentReader, newSchemeWriter, newZippedDataOutput);
return new NewData(newSchemeWriter.toString(), newDataOutput.toByteArray());
}
} finally {
if (oldContent != null) {
try {
oldContent.free();
} catch (SQLException e) {}
}
}
}
private void storeNewData(PreparedStatement insertNewContent, long id, String newScheme, byte[] newData) throws Exception {
try (
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
){
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
public void migrate() throws Exception {
List<Long> ids = loadIds();
try (
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
){
for (Long id : ids) {
NewData newData = generateNewDataFromOldContent(selectOldContent, id);
storeNewData(insertNewContent, id, newData.scheme, newData.data);
}
}
}
}
public class MigratorV4 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
@RequiredArgsConstructor
private static class NewData {
final String scheme;
final byte[] data;
}
@RequiredArgsConstructor
private static class SmartXMLStreamWriter implements AutoCloseable {
final XMLStreamWriter writer;
@Override
public void close() throws Exception {
writer.close();
}
}
@RequiredArgsConstructor
private static class SmartBlob implements AutoCloseable {
final Blob blob;
@Override
public void close() throws Exception {
blob.free();
}
}
private List<Long> loadIds() throws Exception {
List<Long> ids = new ArrayList<>();
try (ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data")) {
while (oldIdResult.next()) {
ids.add(oldIdResult.getLong(1));
}
}
return ids;
}
private Blob loadOldContent(PreparedStatement selectOldContent, long id) throws Exception {
selectOldContent.setLong(1, id);
try (ResultSet oldContentResult = selectOldContent.executeQuery()) {
oldContentResult.next();
return oldContentResult.getBlob(1);
}
}
private void oldContentToNewData(Reader oldContentReader, StringWriter newSchemeWriter, GZIPOutputStream newZippedDataOutput) throws Exception {
try (
SmartXMLStreamWriter newSchemeXMLWriter = new SmartXMLStreamWriter(xmlFactory.createXMLStreamWriter(newSchemeWriter));
SmartXMLStreamWriter newDataXMLWriter = new SmartXMLStreamWriter(xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8"));
){
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
}
}
private NewData generateNewDataFromOldContent(PreparedStatement selectOldContent, long id) throws Exception {
try (
SmartBlob oldContent = new SmartBlob(loadOldContent(selectOldContent, id));
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.blob.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
){
oldContentToNewData(oldContentReader, newSchemeWriter, newZippedDataOutput);
return new NewData(newSchemeWriter.toString(), newDataOutput.toByteArray());
}
}
private void storeNewData(PreparedStatement insertNewContent, long id, String newScheme, byte[] newData) throws Exception {
try (
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
){
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
public void migrate() throws Exception {
List<Long> ids = loadIds();
try (
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
){
for (Long id : ids) {
NewData newData = generateNewDataFromOldContent(selectOldContent, id);
storeNewData(insertNewContent, id, newData.scheme, newData.data);
}
}
}
}
public class MigratorV5 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
@RequiredArgsConstructor
private static class NewData {
final String scheme;
final byte[] data;
}
private List<Long> loadIds() throws Exception {
List<Long> ids = new ArrayList<>();
try (ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data")) {
while (oldIdResult.next()) {
ids.add(oldIdResult.getLong(1));
}
}
return ids;
}
private Blob loadOldContent(PreparedStatement selectOldContent, long id) throws Exception {
selectOldContent.setLong(1, id);
try (ResultSet oldContentResult = selectOldContent.executeQuery()) {
oldContentResult.next();
return oldContentResult.getBlob(1);
}
}
private void oldContentToNewData(Reader oldContentReader, StringWriter newSchemeWriter, GZIPOutputStream newZippedDataOutput) throws Exception {
XMLStreamWriter newSchemeXMLWriter;
XMLStreamWriter newDataXMLWriter;
try (
AutoCloseable fake1 = (newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter))::close;
AutoCloseable fake2 = (newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8"))::close;
){
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
}
}
private NewData generateNewDataFromOldContent(PreparedStatement selectOldContent, long id) throws Exception {
Blob oldContent;
try (
AutoCloseable fake = (oldContent = loadOldContent(selectOldContent, id))::free;
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
){
oldContentToNewData(oldContentReader, newSchemeWriter, newZippedDataOutput);
return new NewData(newSchemeWriter.toString(), newDataOutput.toByteArray());
}
}
private void storeNewData(PreparedStatement insertNewContent, long id, String newScheme, byte[] newData) throws Exception {
try (
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
){
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
public void migrate() throws Exception {
List<Long> ids = loadIds();
try (
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
){
for (Long id : ids) {
NewData newData = generateNewDataFromOldContent(selectOldContent, id);
storeNewData(insertNewContent, id, newData.scheme, newData.data);
}
}
}
}
public class MigratorV6 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
@RequiredArgsConstructor
private static class NewData {
final String scheme;
final byte[] data;
}
private List<Long> loadIds() throws Exception {
List<Long> ids = new ArrayList<>();
try (ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data")) {
while (oldIdResult.next()) {
ids.add(oldIdResult.getLong(1));
}
}
return ids;
}
private Blob loadOldContent(PreparedStatement selectOldContent, long id) throws Exception {
selectOldContent.setLong(1, id);
try (ResultSet oldContentResult = selectOldContent.executeQuery()) {
oldContentResult.next();
return oldContentResult.getBlob(1);
}
}
private void oldContentToNewData(Reader oldContentReader, StringWriter newSchemeWriter, GZIPOutputStream newZippedDataOutput) throws Exception {
@Cleanup XMLStreamWriter newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter);
@Cleanup XMLStreamWriter newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8");
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
}
private NewData generateNewDataFromOldContent(PreparedStatement selectOldContent, long id) throws Exception {
@Cleanup("free") Blob oldContent = loadOldContent(selectOldContent, id);
try (
Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
StringWriter newSchemeWriter = new StringWriter();
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
){
oldContentToNewData(oldContentReader, newSchemeWriter, newZippedDataOutput);
return new NewData(newSchemeWriter.toString(), newDataOutput.toByteArray());
}
}
private void storeNewData(PreparedStatement insertNewContent, long id, String newScheme, byte[] newData) throws Exception {
try (
StringReader newSchemeReader = new StringReader(newScheme);
ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
){
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
public void migrate() throws Exception {
List<Long> ids = loadIds();
try (
PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
){
for (Long id : ids) {
NewData newData = generateNewDataFromOldContent(selectOldContent, id);
storeNewData(insertNewContent, id, newData.scheme, newData.data);
}
}
}
}
public class MigratorV7 {
private Connection conn; // Injected
private SAXParser xmlParser; // Injected
private XMLOutputFactory xmlFactory; // Injected
public void migrate() throws Exception {
@Cleanup PreparedStatement selectOldContent = conn.prepareStatement("select content from old_data where id = ?");
@Cleanup PreparedStatement insertNewContent = conn.prepareStatement("insert into new_data (id, scheme, data) values (?, ?, ?)");
@Cleanup ResultSet oldIdResult = conn.createStatement().executeQuery("select id from old_data");
while (oldIdResult.next()) {
long id = oldIdResult.getLong(1);
selectOldContent.setLong(1, id);
@Cleanup ResultSet oldContentResult = selectOldContent.executeQuery();
oldContentResult.next();
@Cleanup("free") Blob oldContent = oldContentResult.getBlob(1);
@Cleanup Reader oldContentReader = new InputStreamReader(new GZIPInputStream(oldContent.getBinaryStream()));
@Cleanup StringWriter newSchemeWriter = new StringWriter();
@Cleanup XMLStreamWriter newSchemeXMLWriter = xmlFactory.createXMLStreamWriter(newSchemeWriter);
ByteArrayOutputStream newDataOutput = new ByteArrayOutputStream();
@Cleanup GZIPOutputStream newZippedDataOutput = new GZIPOutputStream(newDataOutput);
@Cleanup XMLStreamWriter newDataXMLWriter = xmlFactory.createXMLStreamWriter(newZippedDataOutput, "utf-8");
xmlParser.parse(new InputSource(oldContentReader), new DefaultHandler() {
// Usage of schemeXMLWriter and dataXMLWriter to write XML into String and byte[]
});
String newScheme = newSchemeWriter.toString();
byte[] newData = newDataOutput.toByteArray();
@Cleanup StringReader newSchemeReader = new StringReader(newScheme);
@Cleanup ByteArrayInputStream newDataInput = new ByteArrayInputStream(newData);
insertNewContent.setLong(1, id);
insertNewContent.setCharacterStream(2, newSchemeReader, newScheme.length());
insertNewContent.setBlob(3, newDataInput, newData.length);
insertNewContent.executeUpdate();
}
}
}
К сожалению, не доступен сервер mySQL