coreer

Just another WordPress.com site

Ehcache. Общие понятия

cache — хранилище данных, которые могут понадобиться в ближайшем будущем, и доступ к ним может быть представлен очень быстро.
cache-hit — условие когда мы обратились в кеш и записать там оказалась.
cache-miss — условие когда мы обратились, но записи там не оказалось.
system-of-record (SOR) — это место где мы в любом случае получим точно самое достоверное значение желаемой записи.

Большинство современных приложений развертываются на группе серверов паралельно для улучшения одновременного обслуживания  большого количества пользователей. Для такой среды Ehcache имеет две топологии:
1) Самостоятельная — на конкретной ноде кеш не интересует дела его колег на соседних, и каждый работает сам. В такой топологии между нодами много несоответсвий в данных, она применима только если это для нас не критично.
2) Распределенный Ehcache (Distributed Cache) – данные кеша храняться в Terracotta Server Array, в подмасиве для каждой ноды, в этом случае если на одной ноде что-то было закешировано, то этим кешом смогут пользоваться все ноды. CacheManager-ы на разных JVM(и вообще нодах) могут разделять записи(элементы) между своими кешами.
3) Репликация – данные кеша сохраняются на ноде создателя записи и копируются, или инвалидируются, на других нодах кластера. Для этой топологии возможен только режим слабой связности, потому что он не предоставляет такой скорости распределения как второй.
Реплики могут выполняться следующими инструментами: RMI, JGroups, JMS, Cache Server(это вроде чисто ехкешовая штука).

Основные классы:

CacheManager – это менеджер, который позволяет создавать кеши, получать доступ к ним, удалять кеши, в последних версиях при создание кеш-менеджера для одного и того же имени в конфигурационном обьекте всегда будет возвращаться синглтон кеш-менеджера.

Вот методы получения менеджера:
CacheManager.newInstance(Configuration configuration)  создает новую инстанцию менеджера или возвращает, если уже существует с именем указанным в переданной конфигурации
CacheManager.create() Создает новую инстанцию без имени, или возвращает если уже она была создана до этого. Это тоже самое что и предыдущий метод без атрибута конфигурации.
CacheManager.create(Configuration configuration) строго идентично первому методу.
new CacheManager(Configuration configuration) создает инстанцию или выкидывает ексепшин, если уже есть такой менеджер с указанным в конфигурация имени, или конфигурационный обьект налл.

Ehcache – это интрефейс, который реализуют кеши менеджера. Состоит из имени и атрибутов. Содержит Element-ы. Кеш здесь аналогичен кеш-регионам в других системах кеширования.
Элементы сохраняются в MemotyStore, и если настроено, то в случае переполнения (лимиты указываются в настройках) сбрасывается на DiskStore.

Element – атомарный елемент в кеше. Он состоит из пары ключ-значение и записи доступов. Элементы добавляются в кеш и удаляются оттуда. Они также могут истекать по времени жизни и удаляться из кеша Эхкешем в зависимости от настроек.
В кеше может хранится как объекты реализующие Serializable так несерилируемые Object. Работа с ними буквально идетинтичная, кроме как получения кешируемого  или с помощью getObjectValue, или getKeyValue. Несерилизируемые объекты не могут попадать в DiskStore и реплицироваться, если происходит попытка то не выбрасывается исключение, а выводится в лог уровня DEBUG.

JDBC RowSet

Это расширение резалтсета, которое позволяет более гибко работать со строкой базы данных.
Роусет является бином, поэтому мы можем подписываться на изменения его состояний, это может быть передвижение курсора, обновление какой-нибудь колонки, добавлением новой строки и т.д.

У нас есть два класса RowSet:
Connected:
– JdbcRowSet

Disconnected:
– CachedRowSet
– JoinRowSet
– FilteredRowSet

JDBC Statement & ResulSet

И так, чтобы начать получать данные из  базы или сохранять/изменять их в ней, нам нужно создать выражение, и выполнить его, результатом выполнения выражения является ResultSet.

ResultSet бывают нескольких типов, которые определяют методы работы с ним:
TYPE_FORWARD_ONLY – курсор такого резалтсета может двигаться только вперед, при этом данные в этом резалтсете храняться на момент выполнения выражения, не отображая изменения в базе на поточный момент работы в ним через курсор.
TYPE_SCROLL_INSENSITIVE – такой резалтсет позволяет курсору двигаться в любом направлении и в абсолютные позиции, но при этом в нем хранятся данные на момент выполнения выражения.
TYPE_SCROLL_SENSITIVE – курсор такого сета тоже подвижен в любом направлении, но при этом изменения в базе тут же отображаются в резалтсете.

Еще одна характеристика резалтсета это конкурентность(ResultSet Concurrency). Она определяет можно ли изменять содержимое базы данных через резалтсет. Есть два типа:
CONCUR_READ_ONLY – не можно.
CONCUR_UPDATABLE – можно изменять изменять содержимое базы.

public void modifyPrices(float percentage) throws SQLException {
Statement stmt = null;
try {
stmt =
con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = stmt.executeQuery("SELECT * FROM COFFEES");

while (uprs.next()) {
float f = uprs.getFloat("PRICE");
uprs.updateFloat("PRICE", f * percentage);
uprs.updateRow();
}

} catch (SQLException e) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}

И так, как уже стало понятно, чтобы база начала выполнять какие-то операции мы должна работать с выражением, а вернее выполнять через него некие запросы.
Делать мы это можем через следующие методы:

boolean execute(String sql) – выполнить скуель указанный в строке; возращается тру, если мы можем получить резалтсет через .getResultSet() в случае если у нас запрос типа выборка, или же мы можем получить количество обработанных/вставленных строк запросом int getUpdateCount(), если же у нас запросом сформирован резалтсет(была выборка), то этот методв вернет -1, -1 он вернет также если не обработано ни одной строки.
ResultSet executeQuery(String sql) – этот метод у нас для того, чтобы выполнять только запросы выборки.
int executeUpdate(String sql) – а этот метод для того, чтобы выполнять запросы INSERT, UPDATE, или DELETE, или SQL DDL, которые не возвращают никаких результатов.
int[] executeBatch()  – этот метод выполняет пакет запросов, которые мы до этого вставили в наше выражение, возвращает он массив с количеством результатов, которые возвратил каждый запрос пакета.

Впринципе мы можем работать только с екзекьютом, при этом не сразу обрабатывать резалтсеты в служае выборок, мы можем потом подключить использование методов, которые получают результаты пердыдущих запросов:
boolean getMoreResults() – тру если мы можем получить следующий результат методом .getResultSet(), у нас не может быть вызовов .getResultSet() больше чем выло выполнено екзекьютов на этом выражении, если мы вызываем .getResultSet() большее количество раз чем было екзекьютов( если  getMoreResults() == фолс), то у нас будет ексепшин.
boolean getMoreResults(int current) – подвинет курсор резалтсетов на указанную позицию, если вернется тру, то мы можем .getResultSet() получить результат выполненного по счету current запроса.

Курсоры резалтсета:

Они содрежат следующие методы:

next:
previous:
first:
last:
beforeFirst:
afterLast:
relative(int rows):
absolute(int row):

Пакетные запросы.

public void batchUpdate() throws SQLException {

Statement stmt = null;
try {
this.con.setAutoCommit(false);
stmt = this.con.createStatement();

stmt.addBatch(
"INSERT INTO COFFEES " +
"VALUES('Amaretto', 49, 9.99, 0, 0)");

stmt.addBatch(
"INSERT INTO COFFEES " +
"VALUES('Hazelnut', 49, 9.99, 0, 0)");

stmt.addBatch(
"INSERT INTO COFFEES " +
"VALUES('Amaretto_decaf', 49, " +
"10.99, 0, 0)");

stmt.addBatch(
"INSERT INTO COFFEES " +
"VALUES('Hazelnut_decaf', 49, " +
"10.99, 0, 0)");

int [] updateCounts = stmt.executeBatch();
this.con.commit();

} catch(BatchUpdateException b) {
JDBCTutorialUtilities.printBatchUpdateException(b);
} catch(SQLException ex) {
JDBCTutorialUtilities.printSQLException(ex);
} finally {
if (stmt != null) { stmt.close(); }
this.con.setAutoCommit(true);
}
}

Также в пакете можно работать с подготовленными выражениями:

con.setAutoCommit(false);
PreparedStatement pstmt = con.prepareStatement(
"INSERT INTO COFFEES VALUES( " +
"?, ?, ?, ?, ?)");
pstmt.setString(1, "Amaretto");
pstmt.setInt(2, 49);
pstmt.setFloat(3, 9.99);
pstmt.setInt(4, 0);
pstmt.setInt(5, 0);
pstmt.addBatch();

pstmt.setString(1, "Hazelnut");
pstmt.setInt(2, 49);
pstmt.setFloat(3, 9.99);
pstmt.setInt(4, 0);
pstmt.setInt(5, 0);
pstmt.addBatch();

// ... and so on for each new
// type of coffee

int [] updateCounts = pstmt.executeBatch();
con.commit();
con.setAutoCommit(true);

Вставка строк через резалтсет

Как известно инсерты делаются execute, executeUpdate. Но некоторые реляционные базы позволяют делать вставки также через резалтсет:
public void insertRow(String coffeeName, int supplierID,
float price, int sales, int total)
throws SQLException {

Statement stmt = null;
try {
stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE
ResultSet.CONCUR_UPDATABLE);

ResultSet uprs = stmt.executeQuery(
"SELECT * FROM " + dbName +
".COFFEES");

uprs.moveToInsertRow();
uprs.updateString("COF_NAME", coffeeName);
uprs.updateInt("SUP_ID", supplierID);
uprs.updateFloat("PRICE", price);
uprs.updateInt("SALES", sales);
uprs.updateInt("TOTAL", total);

uprs.insertRow();
uprs.beforeFirst();
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}

Подготовленные выражения

public void updateCoffeeSales(HashMap salesForWeek)
throws SQLException {

PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;

String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";

String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";

try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);

for (Map.Entry e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}

Создание из локальной бранчи удаленную. Добавление и удаление тегов

Поточный локальный бранч запушить в удаленный с указанным именем:

git push -u origin NAME_FOR_REMOTE_BRANCH

Добавить новый тэг и сразу его на удаленку:

git tag -a TAG_NAME
git push --tags

Удалить тэг с удаленки и локально:

git tag -d TAG_NAME
git push origin :refs/tags/TAG_NAME

Google Cosure. Вводной экскурс статья

Сохранить rsa креденшиалсы на час

git config --global credential.helper 'cache --timeout=3600'

JDBC. Получение подключения

Для JDBC, чтобы получить подключение используется два метода:
DriverManager. Особенность этого класса в том, что с версии 4.0 он подгружает автоматически все драйвера, которые находятся в класспазе, после первого к нему обращения за конекшином. Конекшин он получает по урл.
DataSource. Более сложный вариант. Но имеет ряд преимуществ. Чтобы он работал администратор должен задеплоить два вида датасорса в работающую систему. 1-й поставщика МРБД, второй поставщика ЕЕ сервера. Создается первый ДС и привязывается через JNDI в javax.naming.InitialContext, создается второй привязывает к себе первый и себя привязывает через JNDI тоже к javax.naming.InitialContext. Чтобы получить конекшин мы создаем обьект javax.naming.InitialContext и по JNDI второго ДС получаем его инстанцию, и теперь через гедКонекшин получаем конекшин указав юзернейм и пароль.

DriverManager

Его характерной особеностъю является то, что мы закрываем стейтмент, а не конекшин:

public Connection getConnection() throws SQLException {

Connection conn = null;
Properties connectionProps = new Properties();
connectionProps.put("user", this.userName);
connectionProps.put("password", this.password);

if (this.dbms.equals("mysql")) {
conn = DriverManager.getConnection(
"jdbc:" + this.dbms + "://" +
this.serverName +
":" + this.portNumber + "/",
connectionProps);
} else if (this.dbms.equals("derby")) {
conn = DriverManager.getConnection(
"jdbc:" + this.dbms + ":" +
this.dbName +
";create=true",
connectionProps);
}
System.out.println("Connected to database");
return conn;
}

public static void viewTable(Connection con, String dbName)
throws SQLException {

Statement stmt = null;
String query = "select COF_NAME, SUP_ID, PRICE, " +
"SALES, TOTAL " +
"from " + dbName + ".COFFEES";
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String coffeeName = rs.getString("COF_NAME");
int supplierID = rs.getInt("SUP_ID");
float price = rs.getFloat("PRICE");
int sales = rs.getInt("SALES");
int total = rs.getInt("TOTAL");
System.out.println(coffeeName + "\t" + supplierID +
"\t" + price + "\t" + sales +
"\t" + total);
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}

Начиная с JDK 7 мы можем упростить последний метод:

public static void viewTable(Connection con) throws SQLException {

String query = "select COF_NAME, SUP_ID, PRICE, " +
"SALES, TOTAL " +
"from COFFEES";

try (Statement stmt = con.createStatement()) {//LOOK HERE!

ResultSet rs = stmt.executeQuery(query);

while (rs.next()) {
String coffeeName = rs.getString("COF_NAME");
int supplierID = rs.getInt("SUP_ID");
float price = rs.getFloat("PRICE");
int sales = rs.getInt("SALES");
int total = rs.getInt("TOTAL");
System.out.println(coffeeName + ", " + supplierID +
", " + price + ", " + sales +
", " + total);
}
} catch (SQLException e) {
JDBCTutorialUtilities.printSQLException(e);
}//LOOK HERE!
}

Это благодаря The try-with-resources Statement

DataSource

Есть три вида ДС:
1) Обычный.
2) Пул.
3) Распределенный.

Второй позволяет не создавать каждый раз новый конекшин, а получать осовободившийся.
Третий обеспечивает комиты транзакции с несколько баз данных, который объединены как я понимаю репликацией. ДС-пул позволяет контролировать комиты, ролбеки и устанавливать автокомит=тру, распределенный это не позволяет за эти следит ДС сам.

Есть стандартизированные имена JNDI для каждого вида:

Обычный “jdbc/billingDB”:

com.dbaccess.BasicDataSource ds = new com.dbaccess.BasicDataSource();
ds.setServerName("grinder");
ds.setDatabaseName("CUSTOMER_ACCOUNTS");
ds.setDescription("Customer accounts database for billing");

Context ctx = new InitialContext();
ctx.bind("jdbc/billingDB", ds);

...

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/billingDB");
Connection con = ds.getConnection("fernanda","brewed");

ДС-пул “jdbc/pool/fastCoffeeDB”:

com.dbaccess.ConnectionPoolDS cpds = new com.dbaccess.ConnectionPoolDS();
cpds.setServerName("creamer");
cpds.setDatabaseName("COFFEEBREAK");
cpds.setPortNumber(9040);
cpds.setDescription("Connection pooling for " + "COFFEEBREAK DBMS");

Context ctx = new InitialContext();
ctx.bind("jdbc/pool/fastCoffeeDB", cpds);

...

com.applogic.PooledDataSource ds = new com.applogic.PooledDataSource();
ds.setDescription("produces pooled connections to COFFEEBREAK");
ds.setDataSourceName("jdbc/pool/fastCoffeeDB");
Context ctx = new InitialContext();
ctx.bind("jdbc/fastCoffeeDB", ds);

...

ctx = new InitialContext();
ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");
Connection con = ds.getConnection(username, password);

Транзакционный ДС “jdbc/pool/fastCoffeeDB”:

com.dbaccess.XATransactionalDS xads = new com.dbaccess.XATransactionalDS();
xads.setServerName("creamer");
xads.setDatabaseName("COFFEEBREAK");
xads.setPortNumber(9040);
xads.setDescription("Distributed transactions for COFFEEBREAK DBMS");

Context ctx = new InitialContext();
ctx.bind("jdbc/xa/distCoffeeDB", xads);

...

com.applogic.TransactionalDS ds = new com.applogic.TransactionalDS();
ds.setDescription("Produces distributed transaction " +
"connections to COFFEEBREAK");
ds.setDataSourceName("jdbc/xa/distCoffeeDB");
Context ctx = new InitialContext();
ctx.bind("jdbc/distCoffeeDB", ds);

...

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/distCoffeesDB");
Connection con = ds.getConnection();

В ДатаСорсе, что характерно закрывается не експрешин, а именно конекшин:

import java.sql.*;
import javax.sql.*;
import javax.ejb.*;
import javax.naming.*;

public class ConnectionPoolingBean implements SessionBean {

// ...

public void ejbCreate() throws CreateException {
ctx = new InitialContext();
ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");
}

public void updatePrice(float price, String cofName,
String username, String password)
throws SQLException{

Connection con;
PreparedStatement pstmt;
try {
con = ds.getConnection(username, password);
con.setAutoCommit(false);//ПРИЗНАК ТОГО, ЧТО ЭТО ДС-пул
pstmt = con.prepareStatement("UPDATE COFFEES " +
"SET PRICE = ? " +
"WHERE COF_NAME = ?");
pstmt.setFloat(1, price);
pstmt.setString(2, cofName);
pstmt.executeUpdate();

con.commit();//ПРИЗНАК ТОГО, ЧТО ЭТО ДС-пул
pstmt.close();

} finally {
if (con != null) con.close();
}
}

private DataSource ds = null;
private Context ctx = null;
}

Как распознать не сработавший аутоваеред

Когда случается ексепшин вида:

WARN 02.05.13 11:30:32,729 DisposableBeanAdapter [] [] Invocation of destroy method failed on bean with name ‘loyaltyServiceImpl’: java.lang.NullPointerException [main]

То скорее всего у бина loyaltyServiceImpl (LoyaltyServiceImpl) есть @Autowired, который не был найден.

Полезности класса Collections

Обвертки

Есть несколько классов обверток:
1. Первый позволяет обычные коллекции сделать конкурентными.
2. Второй позволяет эти же коллекции сделать неизменяемыми.
3. Третий метод-фабрика позволяет обвернуть коллекции, и если в момент рантайма в коллекцию попытаются добавить объект другого типа чем ожидает коллекция, будет ексепшин. Collections.checked

Удобства

Collections.nCopies(Integer quantity, Type valueForAll);
Collections.singleton(Type element);// создать коллекцию из одного объекта.
Collections.emptySet();//когда нам нужно использовать некий метод, который ожидает коллекцию в параметры, а на этот параметр не важен, мы используем эти методы 
Collections.emptyList();
Collections.emptyMap();

Arrays.asList(new Type[]{…});// так мы получаем обвертку вокруг массива, изменять массив мы можем как через обертку, так и массив напрямую, обвертка не позволяет добавлять/удалять новые элементы(изменять размер массива), выбрасывая исключения(неподдерживаемая операция)

Динамическое распределение памяти

При запуске процесса, его обьекты размещаются динамически (на этапе рантайм) в специально выделенной под эти нужны обвласти в опереративной памяти процесса, под названием куча.
Когда процесс запрашивает опеределенный обьем памяти в случае его наличия ОС выделяет эту память. Каждая процедура процесса должна по итогам своего выполенения освобождать выделенную память, если будут происходить утечки (процедура закончила выполнение, но где-то потеряла место освободжения), стек может переполниться и нарушить работу операционной системы.
Существует также проблема дефрагментации кучи, это когда по общему обьему памяти какбы хватает, но поскольку мы запрашиваем под массив достаточно много памяти, и нигде нет такого последовательного блока байт, мы не можем получить память под наш массив.
В случае сборщика муссора, кроме того, что он отслеживает память, которая потеряла ссылки на нее из всех мест процесса, и освобождет ее, он еще в случае необходимости делает дефрагментацию кучи.
В стандарте позикс Си имеет такие процедуры для работы с памятью:

#include 

void *malloc (size_t size);//выделить столько то байт
void *calloc (size_t nelem, size_t elsize);//выделить столько то элементов по столько то байт
void *realloc(void *ptr, size_t size);/*переопределить выделенную последовательность байт,
если было больше, то лишние байты освбождаются, если стало больше и за старой последовательностью
нет больше места, то эта память копируется в место где есть только последовательных свободных байт
и старое место освобождается*/
void free(void *block);//освободить занятый блок

В C++ методов меньше — new и delete