1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/topfoxs-topfox-sample

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md
userService.listObjects(
    userService.where()  // 等同于 Condition.create() 创建一个条件匹配器对象
        .eq("concat(name,id)","A1")          //生成 (concat(name,id) = 'A1')
        .eq("concat(name,id)","C1","D2","E3")//生成 AND (concat(name,id) = 'C1' OR concat(name,id) = 'D2' OR concat(name,id) = 'E3' )
);
return listUsers;
}

Генерация WHERE условия:

SELECT id, code, name, password, sex, age, amount, mobile, isAdmin, loginCount, lastDate, deptId, createUser, updateUser
FROM users a
WHERE (concat(name, id) = 'A1')
AND (concat(name, id) = 'C1'
OR concat(name, id) = 'D2'
OR concat(name, id) = 'E3')

3.3. Расширенный поиск с группировкой, сортировкой, пользовательскими полями выбора и указанием на страницы

Использование конструкторов запросов EntitySelect и Condition:

/**
 * 核心使用 继承了 topfox 的SimpleService
 */
@Service
public class CoreService extends SimpleService<UserDao, UserDTO> {
    public List<UserDTO> demo2(){
        List<UserDTO> listUsers=listObjects(
                select("name, count('*')") //通过调用SimpleService.select() 获得或创建一个新的 EntitySelect 对象,并返回它
                        .where()         //等同于 Condition.create()
                        .eq("sex","男")  //条件匹配器自定义条件 返回对象 Condition
                        .endWhere()      //условия завершения 返回对象 EntitySelect
                        .orderBy("name") //установка поля сортировки 返回 объекта EntitySelect
                        .groupBy("name") //установление поля группировки 返回 объекта EntitySelect
                        .setPage(10,5)    //указание на страницу (запрос 10-й страницы, возврат 5 записей на страницу)

        );
        return listUsers;
    }
}

Вывод SQL:

SELECT name, count('*')
FROM users a
WHERE (sex = '男')
GROUP BY name
ORDER BY  name
LIMIT 45,5

3.4. Как избежать чтения кэша при запросе

TopFox реализует кэширование, текущий поток имеет кэш первого уровня, Redis — кэш второго уровня.

Чтобы избежать чтения из кэша, когда он включён, установите readCache в false. Это гарантирует, что данные, полученные из запроса, будут идентичны данным в базе данных. Ниже приведены пять примеров:


@RestController
@RequestMapping("/demo")
public class DemoController  {
    @Autowired 
    UserService userService;
    
    @TokenOff
    @GetMapping("/test1")
    public Object test1(UserQTO userQTO) {
        //Пример 1: запрос по id, передав false во втором параметре, чтобы не читать кэш первого/второго уровня
        UserDTO user = userService.getObject(1, false);

        //Пример 2: запрос по нескольким id, необходимо поместить запрашиваемые id в Set контейнер
        Set setIds = new HashSet();
        setIds.add(1);
        setIds.add(2);
        //передавая false во втором параметре, можно избежать чтения кэшей первого и второго уровней
        List<UserDTO> list = userService.listObjects(setIds, false);

        //Пример 3: использование QTO для установки запрета на чтение кэша
        list = userService.listObjects(
            userQTO.readCache(false) //запрет на чтение из кэша (обратите внимание, что это не чтение/запись), установка readCache на QTO в значение false, возвращает себя (QTO)
        );
        //или можно записать так:
        userQTO.readCache(false);
        list = userService.listObjects(userQTO);

        //Пример 4: использование ConditionMatcher для установки запрета на чтение кэша
        list = userService.listObjects(
            Condition.create()     //создание ConditionMatcher
                .readCache(false)  //запрет на чтение из кэша
        );

        return list;
    }
}

3.5. Кэш запросов: различия между thread-cache, redis-cache и readCache

Пожалуйста, сначала прочитайте раздел «Конфигурация TopFox».

Кэш первого уровня больше, чем readCache
Кэш второго уровня больше, чем readCache

Это означает, что если отключить кэш первого и второго уровня, установив readCache в true, то данные всё равно не будут считываться из кэша. Все способы запросов также не будут использовать кэш.

3.6. Включение кэша первого уровня

  • Кэш первого уровня по умолчанию отключён.

Можно включить кэш только для операций одного сервиса:

@Service
public class UserService extends SimpleService<UserDao, UserDTO> {
    @Override
    public void init() {
        sysConfig.setThreadCache(true); //включение кэша первого уровня
    }

Для глобального включения кэша первого уровня добавьте в файл проекта application.properties:

top.service.thread-cache=true
  • После включения кэша первого уровня:
  1. Кэш первого уровня действует только в текущем потоке, и он исчезает после завершения потока.
  2. После включения кэша в следующих примерах user1, user2 и user3 являются одним и тем же экземпляром:
  3. Идея кэша первого уровня взята из механизма сохранения объектов в Hibernate.
@RestController
@RequestMapping("/demo")
public class DemoController  {
    @Autowired
    UserService userService;

    @TokenOff
    @GetMapping("/test2")
    public UserDTO test2() {
        UserDTO user1 = userService.getObject(1);//запрос и сохранение в кэш первого и второго уровня
        UserDTO user2 = userService.getObject(1);//получение из кэша первого уровня
        userService.update(user2.setName("张三"));
        UserDTO user3 = userService.getObject(1);//получение из кэша первого уровня
        return user3;
    }
}
``` **Публичный класс UserService расширяет SimpleService<UserDao, UserDTO>**

@Override
public void init() {
    sysConfig.setRedisCache(true); // открываем одноуровневый кэш
}

**Глобально включаем одноуровневый кеш, добавляем в файл конфигурации проекта application.properties:**

top.service.redis-cache=true

* * *

**Примечание:**

 После включения сначала будет считываться одноуровневый кеш. Если там ничего не найдётся, то будет считываться двухуровневый кеш. И только если и там ничего нет, данные будут извлекаться из базы данных.

 После включения все операции добавления, изменения и удаления в сервисе TopFox будут автоматически синхронизироваться с Redis.

## 3.8. QTO с постфиксом для расширенного поиска
Мы модифицируем исходный код UserQTO:

```java
@Setter
@Getter
@Table(name = "users")
public class UserQTO extends DataQTO {
    private String id;            // идентификатор пользователя, совпадает с именем поля данных

    private String name;          // имя пользователя name, совпадает с именем поля данных
    private String nameOrEq;      // постфикс имени пользователя OrEq
    private String nameAndNe;     // постфикс имени пользователя AndNe
    private String nameOrLike;    // постфикс имени пользователя OrLike
    private String nameAndNotLike;// постфикс имени пользователя AndNotLike
    ...
}

— Постфикс имени поля OrEq

Если мы зададим значение nameOrEq как «Чжан Сань, Ли Сы», то исходный код будет выглядеть следующим образом:

package com.test.service;

/**
 * Основной код использования demo1 интегрирует класс SimpleService от TopFox
 */
@Service
public class CoreService extends SimpleService<UserDao, UserDTO> {
    public List<UserDTO> demo1(){
        UserQTO userQTO = new UserQTO();
        userQTO.setNameOrEq("Чжан Сань, Ли Сы");// здесь присваиваем значение
        // на основе QTO поиск listObjects автоматически генерирует SQL, не нужно настраивать xxxMapper.xml
        List<UserDTO> listUsers = listObjects(userQTO);
        return listUsers;
    }
}

Будет сгенерирован следующий SQL:

SELECT ... FROM SecUser WHERE (name = 'Чжан Сань' OR name = 'Ли Сы')

— Постфикс имени поля AndNe

Если мы зададим значение nameAndNe как «Чжан Сань, Ли Сы», будет сгенерирован SQL:

SELECT ... FROM SecUser WHERE (name <> 'Чжан Сань' AND name <> 'Ли Сы')

— Постфикс имени поля OrLike

Если мы зададим значение nameOrLike как «Чжан Сань, Ли Сы», будет создан SQL:

SELECT ... FROM SecUser WHERE (name LIKE CONCAT('%','Чжан Сань','%') OR name LIKE CONCAT('%','Ли Сы','%'))

— Постфикс имени поля AndNotLike

Если мы зададим значение nameAndNotLike как «Чжан Сань, Ли Сы», будет сгенерирован SQL:

SELECT ... FROM SecUser WHERE (name NOT LIKE CONCAT('%','Чжан Сань','%') AND name NOT LIKE CONCAT('%','Ли Сы','%'))

Приведённые выше примеры являются полностью автоматическими SQL-запросами, сгенерированными TopFox.

3.9. Дополнительные методы поиска

Response< List < DTO > > listPage(EntitySelect entitySelect) List< Map < String, Object > > selectMaps(DataQTO qto)
List< Map < String, Object > > selectMaps(Condition where) List< Map < String, Object > > selectMaps(EntitySelect entitySelect) selectCount(Condition where) selectMax(String fieldName, Condition where) и т. д.

3.10. Пользовательские условия обновления updateBatch

— @param xxxDTO — данные для обновления, обновляются только непустые поля. Поле Id нельзя передавать со значением. — @param where — условие соответствия. — @return List< DTO > — обновлённый набор dto.

@Service
public class UnitTestService {
    @Autowired UserService userService;
    
    public void test(){
        UserDTO dto = new UserDTO();
        dto.setAge(99);
        dto.setDeptId(11);
        dto.addNullFields("mobile, isAdmin");// устанавливаем указанные поля в null

        List<UserDTO> list userService.updateBatch(dto, where().eq("sex","男"));
        // list содержит обновлённые записи
    }
}

Сгенерированный SQL-запрос выглядит следующим образом:

UPDATE users SET deptId=11,age=99,mobile=null,isAdmin=null WHERE (sex = '男')

3.11. Другие примеры вставки и обновления кода

@Service
public class UnitTestService {
    @Autowired UserService userService;
    ... 
    
    public void insert(){
        // Id автоматически увеличивается базой данных, при добавлении можно получить Id
        UserDTO dto = new UserDTO();
        dto.setName("Чжан Сань");
        dto.setSex("мужчина");
        userService.insertGetKey(dto);
        logger.debug("Идентификатор добавленного пользователя: {}", dto.getId());
    }

    public void update(){
        UserDTO user1 = new UserDTO();
        user1.setAge(99);
        user1.setId(1);
        user1.setName("Лоупин");

        // устанавливаем указанные поля в null, допускается наличие пробелов
        user1.addNullFields(" sex , lastDate , loginCount");
//        // этот способ также поддерживается
//        user1.addNullFields("sex","lastDate");
//        // и этот способ тоже поддерживается
//        user1.addNullFields("sex, lastDate","deptId");

        userService.update(user1);// обновляем только поля с значениями
    }

    public void update1(){
        UserDTO user1 = new UserDTO();
        user1.setAge(99);
        user1.setId(1);
        user1.setName("Лоупин");

        userService.update(user1);// обновляем только поля с значениями
    }

    public void updateList(){
        UserDTO user1 = new UserDTO();
        user1.setAge(99);
        user1.setId(1);
        user1.setName("Чжан Сань");
        user1.addNullFields("sex, lastDate");

        UserDTO user2 = new UserDTO();
        user2.setAge(88);

``` ```
user2.setId(2);
user2.setName("李四");
user2.addNullFields("mobile, isAdmin");

List list = new ArrayList();
list.add(user1);
list.add(user2);
userService.updateList(list);//只更新有值的字段
}

Данные проверки компонента в действии: повторная проверка

Предположим, что в пользовательской таблице уже есть запись с номером телефона 13588330001. Затем мы добавляем ещё одну запись с тем же номером телефона или обновляем номер телефона другой записи до этого номера. В этом случае мы хотим, чтобы программа обнаружила эту ошибку. За это отвечает объект CheckData.

Проверка на то, что номер телефона не может повторяться, может быть выполнена несколькими способами:

3.11.1. Пример один

@Service
public class CheckData1Service extends AdvancedService<UserDao, UserDTO> {
    @Override
    public void beforeInsertOrUpdate(List<UserDTO> list) {
        //Многострочные записи выполняются одной инструкцией SQL для проверки номера телефона на наличие дубликатов и генерируют исключение
        checkData(list)  // 1. list — это данные для проверки на дубликаты
                // 2. checkData — это новый объект CheckData, определённый в SimpleService как TopFox
                .addField("mobile", "手机号")        //добавляет заголовок для поля с ошибкой, если выбрасывается исключение
                .setWhere(where().ne("mobile","*")) //устанавливает дополнительное условие для проверки, которое можно не указывать (номера телефонов со значением * не участвуют в проверке)
                .excute();// генерирует SQL-запрос для проверки и выполняет его, если есть повторяющиеся записи (выбрасывает исключение и откатывает транзакцию)
    }
}

В журнале консоли будет выброшено исключение:

##Это SQL-запрос, сгенерированный TopFox для повторной проверки:
SELECT concat(mobile) result
FROM SecUser a
WHERE (mobile <> '*')
  AND (concat(mobile) = '13588330001')
LIMIT 0,1

14:24|49.920 [4] DEBUG 182-com.topfox.util.CheckData      | mobile {13588330001}
Не удалось вставить повторяющееся значение для ключа «номер телефона».
    at com.topfox.common.CommonException$CommonString.text(CommonException.java:164)
    at com.topfox.util.CheckData.excute(CheckData.java:189)
    at com.topfox.util.CheckData.excute(CheckData.java:75)
    at com.sec.service.UserService.beforeInsertOrUpdate(UserService.java:74)
    at com.topfox.service.AdvancedService.beforeSave2(AdvancedService.java:104)
    at com.topfox.service.SimpleService.updateList(SimpleService.java:280)
    at com.topfox.service.SimpleService.save(SimpleService.java:451)
    at com.sec.service.UserService.save(UserService.java:41)
  • Информация об ошибке содержит «номер телефона», который был указан в .addField («мобильный», «номер телефона»).
  • Если в пользовательской таблице есть две записи, первая с идентификатором 001 и номером телефона 13588330001, а вторая с идентификатором 002 и номером телефона 13588330002, то при изменении номера телефона второй записи на 13588330001 произойдёт дублирование данных. Сгенерированный TopFox запрос для проверки при обновлении будет выглядеть следующим образом:
##Этот SQL-запрос генерируется TopFox при проверке во время обновления:
SELECT concat(mobile) result
FROM SecUser a
WHERE (mobile <> '*')
  AND (concat(mobile) = '13588330001')
  AND (id <> '002')   ## изменяет идентификатор записи пользователя
LIMIT 0,1

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

3.11.2. Дополнительные примеры см. в разделе «Компонент проверки данных»

3.12. Компонент журнала изменений ChangeManager для распределённых транзакций и отката

Журнал изменений можно записать в MongoDB для обеспечения возможности отката распределённых транзакций.

Код для чтения журнала изменений очень прост, и было написано два примера:

@Service
public class UserService extends AdvancedService<UserDao, UserDTO> {
     @Override
    public void afterInsertOrUpdate(UserDTO userDTO, String state) {
        if (DbState.UPDATE.equals(state)) {
            // Пример один:
            ChangeManager changeManager = changeManager(userDTO)
                                .addFieldLabel("name", "用户姓名")  //устанавливает заголовок для этого поля в журнале
                                .addFieldLabel("mobile", "手机号"); //устанавливает заголовок для этого поля в журнале
                    
            //Вывод в формате один, параметры
            logger.debug("Журнал изменений:{}", changeManager.output().toString() );
            //Пример вывода: 
            /**
                Журнал изменений:
                 id:000000,      //идентификатор пользователя
                 用户姓名:开发者->开发者2,
                 手机号:13588330001->1805816881122
            */
            
            // Вывод в формате два, JSON
            logger.debug("Журнал изменений:{}", changeManager.outJSONString() );
            //Пример вывода: c — текущее значение, n — новое значение; o — старое значение
            /**
                Журнал изменений:
                 {
                     "appName":"sec",
                     "executeId":"1561367017351_14",
                     "id":"000000",
                     "data":{
                             "version":{"c":"207","o":206},
                             "用户姓名":{"c":"开发者2","o":"开发者"},
                             "手机号":{"c":"1805816881122","o":"13588330001"}
                     }
                }
            */

            //************************************************************************************
            // Пример два: без использования addFieldLabel для установки заголовка поля в журнале, тогда все ключи в data будут выводиться на английском языке
            logger.debug("Журнал изменений:{}",
``` **Конфигурация TopFox**

Ниже представлены параметры конфигурации, которые настраиваются в файле *application.properties* проекта. Отсутствие настройки параметра означает использование его значения по умолчанию. Значение по умолчанию указано после знака равенства:  

1. **top.log.start="▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼..."**  разделитель начала логирования потока отладки.  
2. **top.log.prefix="# "**  префикс для вывода данных в журнале отладки.  
3. **top.log.end=▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲..."**  разделитель окончания логирования потока отладки.  
4. **top.page-size=100**  количество элементов на странице при разбиении на страницы.  
5. **top.max-page-size=300**  максимальное количество элементов, возвращаемых при запросе без разбиения на страницы, если *pageSize* меньше или равен нулю.  
6. **top.service.thread-cache=false**  включение одноуровневого кэша (кэша потоков), по умолчанию отключено (*false*).  
7. **top.service.redis-cache=false**  включение двухуровневого кэша (*redis*), по умолчанию отключено (*false*), заменяет устаревший параметр *open-redis*.  
8. **top.service.open-redis=false**  устарело, заменено параметром *top.service.redis-cache*.  
9. **top.service.redis-log=flase**  уровень журнала *DEBUG*, определяет, будет ли выводиться в журнал информация об операциях с *redis*. По умолчанию отключено (*false*)  не выводится, *true*  выводится.  
10. **top.redis.serializer-json=true**  поддерживается два типа сериализации *redis*: *true* (*jackson2JsonRedisSerializer*) и *false* (*JdkSerializationRedisSerializer*). Рекомендуется в рабочей среде изменить значение на *false*, чтобы библиотека использовала *JdkSerializationRedisSerializer*. В этом случае необходимо отключить *devtools* (*pom.xml*).  
11. **top.service.update-mode=3**  стратегия сериализации DTO и генерации SQL при обновлении.  
    *Значение 1:* DTO = данные, отправленные пользователем. Генерируется *SQL set field=value* для всех полей, отличных от *null*.  
    *Значение 2:* DTO = исходные данные + данные, отправленные пользователем. Генерируется *SQL set field=value* только для изменённых полей.  
    *Значение 3:* DTO = исходные данные + данные, отправленные пользователем. Генерируется *SQL set field=value* для всех изменённых полей, включая добавленные. Это гарантирует, что все поля, отправленные клиентом, будут включены в *SQL*, независимо от того, были они изменены или нет.  
12. **top.service.select-by-before-update=false**  при значении *top.service.update-mode* равном *1* этот параметр определяет, выполняется ли запрос перед обновлением. Если требуется получить журнал изменений и включён *redis*, рекомендуется установить значение *true*.  
13. **top.service.update-not-result-error=true**  генерирует ли исключение при попытке обновить запись, если результат выполнения *SQL* равен нулю. По умолчанию *true*  генерируется, *false*  не генерируется.  
14. **top.service.sql-camel-to-underscore=OFF**  определяет, как имена столбцов преобразуются в *SQL*. Возможные значения: *OFF*  используется исходное имя, *ON-UPPER*  преобразуется в верхний регистр и подчёркивание, *ON-LOWER*  преобразуется в нижний регистр и подчёркивание.  

**Объект SysConfig**  

Интерфейс *SysConfig* имеет реализацию *com.topfox.util.SysConfigDefault*. Методы интерфейса являются методами *set* и позволяют изменять значения параметров во время выполнения программы. Каждый сервис имеет свою копию *SysConfig*, и изменения, внесённые через метод *set*, влияют только на текущий сервис.  

Пример использования:  

Параметр *open-redis* можно изменить следующим образом:  

```java
@Service
public class UserService extends AdvancedService<UserDao, UserDTO> {
    @Override
    public void init() {
        sysConfig.setOpenRedis(false);
    }
}

Это позволит отключить синхронизацию данных UserService с Redis. Другие сервисы продолжат использовать эту функцию.

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/topfoxs-topfox-sample.git
git@api.gitlife.ru:oschina-mirror/topfoxs-topfox-sample.git
oschina-mirror
topfoxs-topfox-sample
topfoxs-topfox-sample
master