У каждой сессии HIBERNATE имеется свой КЭШ первого уровня или по-другому контекст персистности. Этот кэш предназначен для отслеживания изменения состояния персистных объектов (entity и collections), с тем чтобы в нужный момент автоматически перенести эти изменения в БД.
Впрочем, для сессий типа StatelessSession персистность не поддерживается и поэтому у такой сессии кэша первого уровня нет.
Объект становится персистным автоматически при использовании методов HIBERNATE по загрузке объектов, таких как find(), getReference(), Query.getResultList(), Query.getSingleResult(). Или персистным можно сделать объект, с помощью метода persist(). Когда объект становится персистным, то он присоединяется к КЭШ первого уровня.
Персистный объект может быть отсоединен от кэша и тогда он перестает быть персистным. Это может быть сделано с помощью методов detach(), close(), clear() и других.
В HIBERNATE есть некоторые утилиты для просмотра содержимого кэша. Прежде всего это интерфейс SessionStatistics. В этом интерфейсе предусмотрены следующие функции:
- getEntityCount() – возвращает количество entity в кэше
- getCollectionCount() – возвращает количество коллекций в кэше
- getEntityKeys() – Set объектов, в которых информация о ID entity
- getCollectionKeys() – Set объектов, в которых информация о ID entity, которой принадлежит коллекция.
Просмотр содержимого кэш может пригодиться при поиске ошибок и выяснении причин неожиданного замедления в работе HIBERNATE, когда некоторые изменения в программе или настройках приводят к неожиданному изменению в автоматической работе HIBERNATE.
Для просмотра содержимого кэша сессии можно ограничится сведениями о количестве entity и коллекций.
Для более развернутой информации о количестве entity и коллекций каждого класса и о значениях ID entity можно использовать прилагаемую здесь утилиту showSessionCashe8 (фактически метод).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
/** * функция выводит информацию о содержимом кэша сессии HIBERNATE * необходима java8 и старше * @param session сессия HIBERNATE * @param maxCountID - определяет формат вывода информации о содержимом кэша: * отрицательные значения - выводится только количество entity и коллекций * 0 - выводится развернутая информация, но без перечисления id значений entity * положительные значения - выводится развернутая информация, с указанием id значений entity, но выводится не более maxCountID значений id */ public static void showSessionCaсhe8( Session session, int maxCountID ) { int IDMaxLength = 50; // максимальное количество знаков, показываемых у ID // ---- собираем статистику по сессии HIBERNATE ------------ SessionStatistics ss = session.getStatistics(); // собираем статистику по сессии HIBEERNATE if ( maxCountID < 0 ) System.out.println( ss.toString() ); // если maxCountID отрицательный, то выводим краткую информацию о содержимом кэша else { // если maxCountID неотрицательный, то выводим развернутую информацию о содержимом кэша Long timeBeg = System.currentTimeMillis(); // фиксируем начальное время, для определения времени, необходимого для работы данного метода String _text = "\r\n"; // в эту переменную будем собирать диагностический текст try{ // --------------------- показываем сколько всего персистных объектов и персистных коллекций ----------------------------- _text += "\r\n|--(поток "+Thread.currentThread().getName()+") "; _text += ss.getEntityCount() + " entities; "; _text += ss.getCollectionCount() + " collections " + " в КЭШ СЕССИИ HIBERNATE-------------------------\r\n"; /****************************************************************************************** * информация о персистных entities *******************************************************************************************/ // --------------------- определяем сколько персистных объектов каждого java-класса ----------------------------- _text += ((Set<EntityKey>)ss.getEntityKeys()) // получаем список всех EntityKey в контексте персистности .stream() // перебираем все EntityKey кэша (один entity - один EntityKey) .collect(Collectors.groupingBy(ek->ek.getEntityName())) // группируем EntityKey по именам entity, получаем Map<String(имя entity), <List<EntityKey>>> .entrySet().stream() // перебираем вхождения Map, (ключей в Map столько, сколько разных имен entities в кэш) .collect( Collector.of( // создаем собственный коллектор ()->new StringBuilder( "| Entities: \r\n" ), // начальное действие ( r, e )-> // это выполняем для каждого класса entity r.append( // добавляем в буфер сведения о классе entity "| "+ e.getValue().size() + " entities " + e.getKey() + // для каждого класса entity выводим имя entity (e.getKey()) и количество (e.getValue().size()) ((maxCountID==0)? // если запрошена детальная информация, то перечисляем ID entity данного класса "" : // детальная информация не запрошена "; id" + e.getValue().stream().limit( maxCountID ) // e.getValue() - это список EntityKey entity данного класса (его имя в e.getKey()) .map( v-> { // переходим от EntityKey к строке, в которой id + пометка r, если entity помечен на удаление String IDvar = v.getIdentifier().toString(); // id entity if ( IDvar.length() > IDMaxLength) IDvar = IDvar.substring(0, IDMaxLength)+"..."; // если id получился длинный (например у композитного ключа), то обрезаем return IDvar + (session.contains(e.getKey(), session.get(e.getKey(), v.getIdentifier()) )? "": "r" ); // если entity присутствует в контексте персистности и отсутствует в сессии, значит он помечен на удаление } ) .collect(Collectors.joining("; ", "{", ((maxCountID<e.getValue().size())?";...}":"}") ))) + "\n" // перечислем ID и в скобках {} ), ( r1, r2 ) -> null, // выполнение данного коллектора в многопоточном режиме не предусмотрено StringBuilder::toString // заключительная операция - переводим StringBuilder в String )); // TODO вывод для id - Object /****************************************************************************************** * информация о персистных коллекциях *******************************************************************************************/ // --------------------- определяем сколько персистных коллекций каждого java-класса ----------------------------- PersistenceUnitUtil persistenceUtil = session.getEntityManagerFactory() .getPersistenceUnitUtil(); // получаем ссылку, для определения загружена ли коллекция или она не загружена, а представлена proxy /* * тип (role) коллекции указателей на детали представлена как * [имя класса].[имя коллекции] * получим имя класса в переменную masterClassName и имя коллекции * в detaileClassName */ _text += ((Set<CollectionKey>)ss.getCollectionKeys()).stream() // перебираем все CollectionKey кэша .collect(Collectors.groupingBy(kk->kk.getRole())) // группируем CollectionKey по именам entity, получаем Map<String, <List<EntityKey>>> .entrySet().stream() // получаем Stream вхождений в Map группировки .collect( Collector.of( // создаем собственный коллектор ()->new StringBuilder( "|\r\n| Collections: \r\n" ), // начальная строчка ( r, e )-> { r.append("| "+ e.getValue().size() + " collections " + e.getKey() + "; " ); // показываем общую информацию о коллекция if (maxCountID!=0) { // если надо перечислять ID entity, которым принадлежат коллекции String[] arrayRoleName = e.getKey().split("\\."); // ----- получаем составные части типа(role), которые были разделены через точку String masterClassName = Stream.of( arrayRoleName ).limit(arrayRoleName.length - 1).collect(Collectors.joining(".")); // получаем имя entity String detaileClassName = arrayRoleName[ arrayRoleName.length - 1 ]; // получаем имя коллекции r.append( e.getValue().stream() .collect(Collectors.groupingBy( v-> { // разделяем Stream на три Stream - в одном инициированные коллекции, в другом proxy, в третьем коллекции entity, помеченных на удаление if ( !session.contains(masterClassName, session.get(masterClassName, v.getKey())) ) return " from removed entity"; else return persistenceUtil.isLoaded( session.get( masterClassName, v.getKey()), // получаем информацию - загружена ли коллекция или представлена черех proxy detaileClassName)? " real у id":" proxy у id"; })) .entrySet().stream() // перебираем вхождения в Map с тремя ключами (from removed entity, true и false). значениями являются коллекция entity, у которых данная коллекция загружена или proxy .map( es-> { if (es.getValue().size()==0 ) return ""; // если нет proxy или загруженных и removed, то не выводим информацию о них else return // если есть, то собираем перечисление ID entities es.getValue().stream().limit( maxCountID ).map( vv -> { String IDvar = String.valueOf(vv.getKey()); // id entity if ( IDvar.length() > IDMaxLength) IDvar = IDvar.substring(0, IDMaxLength-3)+"..."; // если id получился длинный (например у композитного ключа), то обрезаем return IDvar; }) .collect(Collectors.joining("; ", es.getKey() + "{", ((maxCountID<e.getValue().size())?";...}":"}") )) + " "; }) .filter( s->s!="") .collect(Collectors.joining("\r\n| ","\r\n| ","\r\n" )) ); } else r.append("\r\n"); }, ( r1, r2 ) -> null, // выполнение данного коллектора в многопоточном режиме не предусмотрено StringBuilder::toString // заключительная операция - переводим StringBuilder в String )); _text += "|\r\n|----- " + (System.currentTimeMillis() - timeBeg ) + " милисек ------------------------------------------------------------------------------------------------\r\n"; } finally { System.out.println( _text ); } } } |
Для использования данной утилиты необходима java8.
Метод выводит информацию на консоль. Вывод производится в самом конце метода и метод легко переписать под вывод в лог или под текстовой возврат метода.
Метод принимает на входе объект сессии HIBERNATE – Session session и параметр int maxCountID.
Последний параметр определяет объем выводимой информации. Если передать отрицательное число, то выводится только количество персистных entity и коллекций в кэш сессии. Если передать ноль, то будет выведена информация о количестве entity и коллекций в разрезе классов. Если передать положительное число, то будyт перечислены ID (с пометкой <r> для entity, помеченных на удаление) для каждого класса entity (но не более maxCountID ID). Кроме того, будет выведена информация о принадлежности коллекции к конкретному entity, с указанием реальной загруженности данных коллекции.
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.