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')
Использование конструкторов запросов 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
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;
}
}
Пожалуйста, сначала прочитайте раздел «Конфигурация TopFox».
Кэш первого уровня больше, чем readCache
Кэш второго уровня больше, чем readCache
Это означает, что если отключить кэш первого и второго уровня, установив readCache в true, то данные всё равно не будут считываться из кэша. Все способы запросов также не будут использовать кэш.
Можно включить кэш только для операций одного сервиса:
@Service
public class UserService extends SimpleService<UserDao, UserDTO> {
@Override
public void init() {
sysConfig.setThreadCache(true); //включение кэша первого уровня
}
Для глобального включения кэша первого уровня добавьте в файл проекта application.properties:
top.service.thread-cache=true
@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.
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)
и т. д.
— @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 = '男')
@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.
Проверка на то, что номер телефона не может повторяться, может быть выполнена несколькими способами:
@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)
##Этот SQL-запрос генерируется TopFox при проверке во время обновления:
SELECT concat(mobile) result
FROM SecUser a
WHERE (mobile <> '*')
AND (concat(mobile) = '13588330001')
AND (id <> '002') ## изменяет идентификатор записи пользователя
LIMIT 0,1
На этом примере читатель может понять, почему TopFox генерирует разные запросы при добавлении и обновлении записей.
Журнал изменений можно записать в 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 )