Показаны сообщения с ярлыком java. Показать все сообщения
Показаны сообщения с ярлыком java. Показать все сообщения

вторник, декабря 11, 2012

Декомпилятор Java

Как-то не доводилось до этого, а тут понадобилось попользоваться декомпилятором Java. Понравился проект Java Decompiler - удобно и красиво получается. Есть отдельное GUI-приложение, а есть плагин для Eclipse.


среда, ноября 28, 2012

Преобразование java-объекта, списка java-объектов в JSON и из JSON

Пусть есть такой простой класс


class Book {
private String name;
private List authors;
        ...
}

Мне нужно отображать экземпляры этого класса либо список таких экземпляров в JSON и получать их из JSON обратно.

Попалась и понравилась библиотека Gson от гугла, с ней такие вещи делаются легко и удобно. К проекту нужно добавить одну jar-ку размером 185KB.

Отображение одного объекта 

Book book = new Book();

book.setName("Test Book");
book.addAuthor("A");
book.addAuthor("Б");

Gson gson = new Gson();
String result = gson.toJson(book);

В результате получаем:
{"name":"Test Book","authors":["A","Б"]}

Восстановление объекта из JSON

book = gson.fromJson(result, Book.class);


Отображение списка объектов

Book book2 = new Book();
book2.setName("Test Book 2");
book2.addAuthor("A2");
book2.addAuthor("Б2");

List books = new ArrayList();
books.add(book);
books.add(book2);
String booksStr = gson.toJson(books);

Получаем:
[{"name":"Test Book","authors":["A","Б"]},{"name":"Test Book 2","authors":["A2","Б2"]}]

Восстановление списка объектов

Type collectionType = new TypeToken>(){}.getType();
List gBooks = gson.fromJson(booksStr, collectionType);

Готово!



пятница, августа 03, 2012

Как сгенерировать SHA-1 дайджест по строке (в том числе и в Android)



public class SecurityUtils {
public static String getSHA1Digest(String str) throws NoSuchAlgorithmException {
if (str == null || str.trim().length() == 0) {
throw new InvalidParameterException("Не указано значение  строки");
}
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] bytes = str.getBytes();
sha.update(bytes);
byte[] digest = sha.digest();

StringBuilder sb = new StringBuilder(40);
for (int i = 0; i < digest.length; i++) {
String hexVal = String.format("%02x", Integer.valueOf((digest[i] + 256) & 0xFF));
sb.append(hexVal);
}

return sb.toString();
}
}

Проверяем:


public class SHA1Test extends TestCase {

public void testSHA1() {
   String sourceString = "12345";
   String expectedResult = "8cb2237d0679ca88db6464eac60da96345513964";
   String result = "";
 
   try {
result = SecurityUtils.getSHA1Digest(sourceString);
} catch (NoSuchAlgorithmException e) {
assertTrue(e.getMessage(), false);
}
 
   assertEquals(expectedResult, result);  
}




пятница, апреля 06, 2012

Отформатировать копейки в рубли с разделителями групп и с точкой

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


private static DecimalFormat currencyFormat;
static {
     currencyFormat = new DecimalFormat();
     DecimalFormatSymbols dfs = currencyFormat.getDecimalFormatSymbols(); 
     dfs.setDecimalSeparator('.'); 
     currencyFormat.setDecimalFormatSymbols(dfs);
     currencyFormat.setMinimumFractionDigits(2); 
     currencyFormat.setMaximumFractionDigits(2); 
     currencyFormat.setGroupingSize(3);
     currencyFormat.setGroupingUsed(true);
}

int amount = ...
double roubles = (double) amount / 100;
return currencyFormat.format( roubles );

понедельник, июля 26, 2010

Как получить курсор из хранимой процедуры Oracle - SimpleJdbcCall

Понадобилось получить набор данных из хранимой процедуры Oracle. Данные получить эти надо в java-приложение.
Посмотрел несколько вариантов, наиболее симпатичным (что, впрочем, предсказуемо) оказался вариант и использованием спрингового SimpleJdbcCall.

Итак, есть такая табличка - разумеется, что здесь всё упрощено насколько это можно - и даже чуть более

CREATE TABLE BOOK (
    ID INTEGER NOT NULL PRIMARY KEY, 
    NAME VARCHAR2(20))

Есть такая вот хранимая процедура

PROCEDURE GET_BOOKS_BY_NAME (book_cursor OUT SYS_REFCURSOR, name_template IN VARCHAR2)
IS
BEGIN
  OPEN book_cursor FOR
    SELECT b.ID as ID, b.NAME as NAME 
      FROM BOOK b
     WHERE b.NAME LIKE name_template
     ORDER BY ID;    
  
END GET_BOOKS_BY_NAME;

Осталась самая малость - получить от этой процедуры содержимое курсора book_cursor.

Для начала, понадобится получить экземпляр SimpleJdbcCall. Его можно создать руками, можно прописать в контексте спринга - по вкусу.

Для представления данных удобно создать отдельный DTO


public class BookItem {
    private Integer id;
    private String name;
    public BookItem() {}
    // getters и setters опущены 
}


На экземпляры этого класса будут отображены данные из получаемого курсора.

А теперь собираем всё вместе и получаем данные


    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("app-config.xml");


        SimpleJdbcCall simpleJdbcCall = context.getBean("simpleJdbcCall", SimpleJdbcCall.class);


        // Этот экземпляр отвечает за отображение набора данных, полученных из
        // базы данных в курсоре, на список объектов
        RowMapper rowMapper = ParameterizedBeanPropertyRowMapper.newInstance(BookItem.class);
        // Указывается имя вызываемой хранимой процедуры
        simpleJdbcCall.withProcedureName("GET_BOOKS_BY_NAME");
        // Связывается курсор, получаемый из хранимой процедуры, с отображателем
        // данных
        simpleJdbcCall.returningResultSet("BOOK_CURSOR", rowMapper);


        // Задаются входные параметры для хранимой процедуры
        SqlParameterSource params = new MapSqlParameterSource().addValue("NAME_TEMPLATE", "NEW%");


        // Теперь производится непосредственный вызов хранимой процедуры.
        // В возвращаемый в результате вызова map помещается имя курсора
        // (параметр типа String) и сами полученные данные в виде ArrayList.
        // Для того, чтобы эти данные из ArrayList разобрать, и используется
        // rowMapper
        Map resultMap = simpleJdbcCall.execute(params);


        // При этом вызове производится отображение полученных данных на тип,
        // указанный в параметре rowMapper
        @SuppressWarnings("unchecked")
        List resultList = (List) resultMap.get("BOOK_CURSOR");


        // А теперь просто работаем с тем, что получили от БД
        System.out.println("Получено записей: " + resultList.size());
        for (BookItem item : resultList) {
            System.out.println(item);
        }
    }

Вот и всё.

вторник, апреля 20, 2010

Apache Commons Lang - ToStringBuilder, формирование строки и форматы

Для определённости, возьём такой простой класс:

class Book {
    private String title;
    private String author;
    private Double price;
    private Date   publDate; 
    ... 
}


Я хочу для этого класса получить toString-like отображение.
Удобный для этого способ - взять и применить ToStringBuilder из библиотеки apache commons lang.

Полное имя этого класса -  org.apache.commons.lang.builder.ToStringBuilder.

Способ №1 - руками переопределить метод toString и, с помощью этого класса, описать способ конструирования строкового отображения объектов. Ничего особо примечательного здесь нет, за исключением удобства, которое добавляет упомянутый ToStringBuilder.

    @Override
    public String toString() {
        return new ToStringBuilder(this)
          .append("title", title)
          .append("author", author)
          .append("price", price)
          .append("date", publDate)
          .toString();
      } 


А вот вид получаемой строки зависит от заданного на момент вызова стиля (формата). Примеры этих форматов - чуть низже.

Способ №2 интереснее - он использует reflection. В этом случае мне достаточно вызвать ToStringBuilder.reflectionToString(мой-объект), не занимаясь перечислением имён полей и их значений вручную.

Например, для такого случая

  Book book = new Book();
  book.setAuthor("Gregory M.");
  book.setTitle("The Secret Book of Secrets");
  book.setPrice(15.03);
  book.setPublDate(null); // это специально, чтобы явно показать null-значение


вызов

  System.out.println(ToStringBuilder.reflectionToString(book));

вернёт такой вот результат:
>>> javaapplication243.Book@10b30a7[title=The Secret Book of Secrets,author=Gregory M.,price=15.03,publDate=] 

Да, вот эти >>> я добавил в результат сам, для удобства (моего) при чтении.

Есть несколько способов повлиять на формат получаемого результата.
Результат, показанный чуть выше, получен при использовании формата, используемого по-умолчанию. Имя этого формата - DEFAULT_STYLE.

Можно руками, вызвав статический метод, указать формат, который я хочу использовать:
  
  ToStringBuilder.setDefaultStyle(ToStringStyle.MULTI_LINE_STYLE);

А можно указать нужный формат явным образом, например, вот так:

  ToStringBuilder.reflectionToString(book, ToStringStyle.MULTI_LINE_STYLE)      

Теперь самое интересное - какие форматы есть и как они влияют на результат.

DEFAULT_STYLE
>>> javaapplication243.Book@10b30a7[title=The Secret Book of Secrets,author=Gregory M.,price=15.03,publDate=]  

MULTI_LINE_STYLE
>>> javaapplication243.Book@10b30a7[
      title=The Secret Book of Secrets
      author=Gregory M.
      price=15.03
      date=
    ]


SHORT_PREFIX_STYLE
>>> Book[title=The Secret Book of Secrets,author=Gregory M.,price=15.03,publDate=]

NO_FIELD_NAMES_STYLE
>>> javaapplication243.Book@10b30a7[The Secret Book of Secrets,Gregory M.,15.03,]

SIMPLE_STYLE
>>> The Secret Book of Secrets,Gregory M.,15.03,         

среда, апреля 14, 2010

Составной первичный ключ из двух FK

Встретился несколько необычный случай отображения данных.
На картинке показаны соответствующие таблицы.





В EVENT_KIND содержится список каких-то видов событий, а в VALID_EVENT_KIND - список допустимых сочетаний "предыдущий вид события" - "следующий тип события".

Если работа с EVENT_KIND типична и ничего интересного не представляет, то вот случай с VALID_EVENT_KIND - интереснее.
Классогенератор Netbeans отказыватеся генерировать для этой таблицы соответствующий Entity, резонно, в некотором смысле, сообщая о том, что это ведь обычная таблица связи (для отношения многие-ко-многим), а поэтому и сущность соответствующая не нужна. А раз не нужна, то и делать не буду (упорный, блин).

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

Итак, раз для нас код сгенерировать отказались, остаётся написать его самому. К счастью, особо тут и писать нечего.

Вот так выглядит класс, соответствующий VALID_EVENT_KIND

@Entity
@Table(name="VALID_EVENT_KIND")
public class ValidEventKind implements Serializable {

    @EmbeddedId
    private ValidEventKindPk pk;

    public ValidEventKind() {}

    public ValidEventKind(EventKind prevEvent, EventKind nextEvent) {
        this.pk = new ValidEventKindPk(prevEvent, nextEvent);
    }
}
Ничего интересного, за исключением @EmbeddedId.
А вот и описание самого первичного, embedded-ного, ключа:

@Embeddable
public class ValidEventKindPk implements Serializable {
    @ManyToOne()
    @JoinColumn(name="prev_event", referencedColumnName="code")
    private EventKind prevEventKind;

    @ManyToOne
    @JoinColumn(name="next_event", referencedColumnName="code")
    private EventKind nextEventkind;

    public ValidEventKindPk() { }

    public ValidEventKindPk(EventKind prevEventKind, EventKind nextEventKind) {
        this.prevEventKind = prevEventKind;
        this.nextEventkind = nextEventKind;
    }
}
Разные getters и setters в обоих классах я, очевидное дело, опустил.

Вот и всё...