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

OSCHINA-MIRROR/noear-weed3

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
文章_Weed3 for java 新的微型ORM框架.md 28 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 26.11.2024 06:09 5b6a449

Weed3 для Java: новый микро-ORM фреймворк

Weed3 — это микро-фреймворк ORM (Object-Relational Mapping), который поддерживает Java SQL, XML SQL, аннотации SQL; шаблонный SQL; транзакции; кэширование; прослушивание и т. д.

Он представляет собой многофункциональный и компактный ORM-фреймворк: его размер составляет 0,2 Мб, и он не зависит от других библиотек. Его внешний интерфейс также невелик, и основная работа выполняется через четыре интерфейса в DbContext.

Особенности и философия Weed3:

  • Высокая производительность: два года назад коллега провёл сравнение четырёх ORM-фреймворков, и Weed3 показал лучшие результаты по производительности (не известно, так ли это сейчас).
  • Кроссплатформенность: Weed3 можно интегрировать в JVM скриптовые движки (JS, Groovy, Lua, Python, Ruby); также существуют версии для .NET и PHP.
  • Компактность: размер Weed3 составляет всего 0,1 Мб (при этом он обладает полной функциональностью и богатым набором возможностей; он может значительно упростить разработку баз данных).
  • Индивидуальность: Weed3 не любит рефлексию и конфигурацию (за исключением подключения, которое не требует настройки).
  • Другие особенности: поддержка управления кэшем и кросс-базовых транзакций.

Компоненты Weed3:

Компонент Описание
org.noear:weed3 Основной фреймворк (без зависимостей)
Опциональный компонент Описание
org.noear:weed3-maven-plugin Maven плагин для генерации Xml SQL маппера
org.noear:weed3-solon-plugin Solon плагин, поддерживающий @Db аннотацию и прямое внедрение Mapper
org.noear:weed3.cache.memcached Расширенная служба кэширования на основе Memcached
org.noear:weed3.cache.redis Расширенная служба кэширования на основе Redis
org.noear:weed3.cache.ehcache Расширенная служба кэширования на основе ehcache
org.noear:weed3.cache.j2cache Расширенная служба кэширования на основе j2cache

Конфигурация Maven:

<!-- Фреймворк пакет -->
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>weed3</artifactId>
    <version>3.4.33</version>
</dependency>

<!-- Maven плагин, используемый для создания Xml SQL мапперов -->
<plugin>
    <groupId>org.noear</groupId>
    <artifactId>weed3-maven-plugin</artifactId>
    <version>3.4.33</version>
</plugin>

Процесс работы с Weed3:

  • Настройка информации о DataSource.
  • Инициализация DbContext.
  • Вызов интерфейсов в DbContext (необходимо ознакомиться с синтаксисом...).

Часть 1. Контекст объекта DbContext

Все операции Weed3 основаны на интерфейсах в DbContext:
    1. После настройки источника данных (или другого формата конфигурации, или службы конфигурации) можно инициализировать DbContext:

    Если используется Spring, можно получить конфигурацию через аннотации.

    В случае использования Solon, конфигурация может быть получена через аннотации или интерфейсы.

// Пример использования свойств для конфигурации:
Properties properties = Solon.cfg().getProp("demo.db"); // Это интерфейс Solon
DbContext db  = new DbContext(properties); 

// Пример использования proxool пула потоков для конфигурации (кажется, сейчас не используется):
DbContext db  = new DbContext("user","proxool.xxx_db"); 

// Пример использования DataSource для конфигурации (обычно используется при использовании пулов соединений, рекомендуется Hikari):
DataSource dataSource = new HikariDataSource(...);
DbContext db  = new DbContext("user", dataSource); 

// Также можно использовать URL, имя пользователя и пароль (в этом случае настройка не требуется):
DbContext db  = new DbContext("user","jdbc:mysql://x.x.x:3306/user","root","1234");

/* Я обычно использую конфигурацию служб, поэтому напрямую получаю объект контекста базы данных из конфигурации. */
// Использование конфигурационной службы для прямого получения объекта контекста базы данных:
DbContext db = WaterClient.Config.get("demo.db").getDb();

Часть 2. Четыре основных интерфейса db.mapper(), db.table(), db.call(), db.sql()

Четыре основных интерфейса также представляют собой четыре различных подхода к использованию DbContext в разных сценариях.

Основные интерфейсы: db.mapper() и db.table(). Они представляют два совершенно разных стиля и вкуса.

Дополнительные интерфейсы: db.call() и db.sql(). Они предназначены для особых сценариев использования.

Эти четыре интерфейса могут быть легко интегрированы в скриптовые движки JVM (JS, Groovy, Lua, Python, Ruby) и некоторые языки GraalVM.
Поскольку у автора также есть встроенный FaaS движок, унифицированный объект выполнения, инъекция без конфигурации и слабо типизированные интерфейсы играют важную роль; они могут быть удобно интегрированы в различные языки и предоставлять унифицированный опыт ORM.
(1) db.mapper(): предоставляет поддержку операций mapper

Стиль mapper сейчас очень популярен. Большинство людей используют его.

Этот интерфейс предоставляет базовый режим Mapper, режим аннотаций @Sql и режим конфигурации Xml sql. В последнем случае внутренняя обработка Xml sql будет предварительно скомпилирована в класс Java во время запуска; производительность должна быть хорошей (похоже на предварительную компиляцию JSP).

    1. db.mapperBase(clz) для получения экземпляра BaseMapper:

    Кажется, что все, у кого нет BaseMapper, немного смущаются называть себя ORM фреймворком.

    Этот интерфейс действительно предоставляет множество методов, полностью избавляя от необходимости писать простой CRUD.

// Непосредственное использование BaseMapper
BaseMapper<User> userDao= db.mapperBase(User.class);

// Вставка:
userDao.insert(user,false); // false: исключить нулевые значения

// Удаление:
userDao.deleteById(12); 

// Обновление: через ID
userDao.updateById(user,false); // false: исключить нулевые значения
// через условие
userDao.update(user,false,m->m.whereEq(User::getType,12).andEq(User::getSex,1));

// Поиск: через ID
User user = userDao.selectById(12);
// поиск: через условие
User user = userDao.selectItem(m -> m.whereEq(User::getId,12));
    1. db.mapper(clz), получение экземпляра Mapper:
@Namespace("demo.dso.db")
public interface UserDao { // Этот интерфейс может расширять BaseMapper<T>
    @sql("select * from `user` where id=@{id}") // стиль переменных
    User getUserById(int id);
  
    @sql("select * from `user` where id=?")         // стиль заполнителя
    User getUserById2(int id);
  
    long addUser(User m); // Нет аннотации, необходимо написать конфигурацию xml sql
}

UserDao userDao = db.mapper(UserDao.class);

User user = userDao.getUserById(12);
userDao.addUser(user);
    1. db.mapper(xsqlid, args), получение результата Xml sql mapper:

    Преимущество этого интерфейса заключается в том, что DAO можно превратить в промежуточное ПО: превращение xml sql в DAO. Текст запроса:

  1. Положить в базу данных, унифицировать управление; и через разработку DAO-шлюза предоставить услуги в виде RPC или REST API.
Map<String, Object> args = new HashMap<>();
args.put("id", 22);

//xsqlid = @{sqlid} = @{namespace}.{id}
User user = db.mapper("@demo.dso.db.getUserById", args);
  1. db.table(), предоставление чистых Java-цепочек операций.

Это первоначальный вид Weed3, и это мой любимый метод. Также это ключевой момент для конкретной кроссплатформенной интеграции.

BaseMapper также реализован внутри db.table(). Это просто несколько строк кода.

Гибкий, эластичный, прямой, может реализовать любой эффект SQL-кода. Очень удобно для разработки бэкенда управления (потому что условия запроса беспорядочны).

Этот интерфейс можно легко интегрировать в движок сценариев JVM (js, groovy, lua, python, ruby) или язык GraalVM.

Интерфейс db.table():

  • Вставка (INSERT):

    • Одна запись:

      User user = new User();
      ...
      // Одна запись
      db.table("user").set("name", "noear").insert();
      db.table("user").setEntity(user).insert();
      db.table("user").setEntityIf(user, (k, v) -> v != null).insert(); // Фильтрация null
      
      // Пакетная вставка
      db.table("user").insertList(list);
    • Удаление (DELETE):

      // Удалить записи с id < 12
      db.table("user").whereLt("id", 12).delete();
  • Обновление (UPDATE):

    • Изменить поле sex для записей с id = 23:

      // Изменить поле sex записей с id=23
      db.table("user").set("sex", 1).whereEq("id", 23).update();
      
      // Добавить или обновить по номеру мобильного телефона
      public void saveUser(UserModel m) {
          db.talbe("user")
              .setEntityIf(m, (k, v) -> v != null)
              .upsert("mobile");
      }
  • Выбор (SELECT):

    • Подсчитать количество записей с id < 100 и длиной имени > 10 символов:

      // Подсчёт количества записей с id<100, длина имени>10
      db.table("user").where("id<?", 100).and("LENGTH(name)>?", 10).count();
      
      // Выбрать 20 записей с id > 10
      db.table("user").whereGte("id", 10).limit(20).selectMapList("*");
      
      // Внутреннее соединение и вывод одной сущности
      db.table("user u")
          .innerJoin("user_ex e")
          .onEq("u.id", "e.user_id")
          .whereEq("u.id", 10)
          .andEq("e.sex", 1)
          .limit(1)
          .selectItem("u.*, e.sex user_sex", User.class);
      
      // Запрос ++ (условное построение):
      var tb = db.table("user u");
      if (test.a) {
          tb.innerJoin("user_ext e").onEq("u.id", "e.user_id");
      }
      
      if (test.b) {
          tb.whereEq("u.id", 1001);
      }
      
      tb.selectItem("u.*, e.sex, e.label", User.class);
      
      // Запрос ++2 (построение функции):
      db.table("user u")
          .build(tb -> {
              if (test.a) {
                  tb.innerJoin("user_ext e").onEq("u.id", "e.user_id");
              }
      
              if (test.b) {
                  tb.whereEq("u.id", 1001);
              }
          })
          .selectItem("u.*, e.sex, e.label", User.class);
    • Обладает фильтрующей способностью: whereIf, andIf, orIf, setIf, setMapIf, setEntityIf.

      • Если есть имя, добавить условие имени; (полезно для бэкенда, экономит много if):

        // Если есть имя, добавляем условие имени
        db.talbe("user").whereIf(name != null, "name=?", name).limit(10).selectMapList("*");
        
        // Вставка, фильтрация null
        db.table("user").setMapIf(map, (k, v) -> v != null).insert(); // фильтрация null
        
        // Обновление
        db.table("user")
            .setIf(name != null, "name", name)
            .setIf(sex > 0, "sex", sex)
            .setIf(mobile != null && mobile.length() = 11, "mobile", mobile)
            .where("id=?", id)
            .update();
  1. db.call(), предоставляет операции вызова:

    • Вызов процедуры:

      User user = db.call("user_get").set("id", 1).getItem(User.class);
    • Вызов SQL:

      //@Sql реализуется внутри
      //
      User user = db.call("select * from user where id=@{id}").set("id", 1).getItem(User.class);
    • Xmlsql вызов:

      // Начинается с @, за которым следует sqlid
      //
      User user = db.call("@demo.dso.db.getUser").set("id", 1).getItem(User.class);
  2. db.sql(), предоставляет ручные операции SQL:

    // В конечном итоге все интерфейсы преобразуются в db.sql(), который является самым нижним интерфейсом
    //
    User user = db.sql("select * from user where id=?", 12).getItem(User.class);
    
    Long total = db.sql("select count(*) from user").getValue(0l);
    
    // Быстрый вариант db.sql(): db.exe(), используется для быстрой пакетной обработки
    //
    db.exe("delete from user where id=12");
    db.exe("update user sex=1 where id=12");

Три, синтаксис Mapper:

  1. BaseMapper интерфейс:

    • Long insert(T entity, boolean excludeNull);
    • void insertList(List<T> list);
    • Integer deleteById(Object id);
    • Integer deleteByIds(Iterable idList);
    • Integer deleteByMap(Map<String, Object> columnMap);
    • Integer delete(Act1<WhereQ> condition);
    • Integer updateById(T entity, boolean excludeNull);
    • Integer update(T entity, boolean excludeNull, Act1<WhereQ> condition);
    • Long upsert(T entity, boolean excludeNull);
    • Long upsertBy(T entity, boolean excludeNull, String conditionFields);
    • boolean existsById(Object id);
    • boolean exists(Act1<WhereQ> condition);
    • T selectById(Object id);
    • List<T> selectByIds(Iterable idList);
    • List<T> selectByMap(Map<String, Object> columnMap);
    • T selectItem(T entity);
    • T selectItem(Act1<WhereQ> condition);
    • Map<String, Object> selectMap(Act1<WhereQ> condition);
    • Object selectValue(String column, Act1<WhereQ> condition);
    • Long selectCount(Act1 condition);

List selectList(Act1 condition);

List<Map<String, Object>> selectMapList(Act1 condition);

List selectArray(String column, Act1 condition);

List selectPage(int start, int end, Act1 condition);

List<Map<String, Object>> selectMapPage(int start, int end, Act1 condition);

(二) annotation sql

  • Пример:
ICacheServiceEx cache = new LocalCache().nameSet("cache");  

//顺带加了缓存  
@Sql(value="select * from user where id=@{id}", caching="cache")  
public UserModel getUser(int id);  
  • Sql 注解说明:
@Target({ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Sql {  
    String value() default "";      //代码  
    String caching() default "";    //缓存服务名称  
    String cacheClear() default ""; //缓存清除标签  
    String cacheTag() default "";   //缓存标签  
    int usingCache() default 0;     //缓存时间  
}  

(三) Xml sql

Пример:

<?xml version="1.0" encoding="utf-8" ?>  
<mapper namespace="weed3demo.xmlsql2">  
    <sql id="getUser" :return="demo.model.UserModel" :note="获取用户信息">  
        SELECT * FROM user WHERE id = @{id:int}  
    </sql>  
</mapper>  

Синтаксис:

mapper 开始标签  
  namespace (属性:命名空间,{namespace}.{id} = sqlid;不包括文件名)  
  import(属性:导入包或类,多个以;号隔开。可以简化后面的模型写法,或引入工具类)  
  baseMapper(属性:扩展BaseMapper 的模型,效果:BaseMapper<Xxx>)  
  db(属性:关联 dataSource bean;效果:@Db("xxx"))  

sql 代码块定义指令  
  id  
  param?(属性:外部输入变量申明;默认会自动生成)  
  declare?(属性:内部变量类型预申明)  
  return(属性:返回类型)  

  remarks(属性:描述、摘要)  

  caching?(属性:缓存服务name) //是对 ICacheController 接口的映射  
  cacheClear?(属性:清除缓存)  
  cacheTag?(属性:缓存标签,支持在入参或结果里取值替换)  
  usingCache?(属性:缓存时间,int)  

if 判断控制指令(没有else)  
  test (属性:判断检测代码)  
     //xml避免语法增强:  
     //lt(<) lte(<=) gt(>) gte(>=) and(&&) or(||)  
        //例:m.sex gte 12 :: m.sex >=12  
     //简化语法增强:  
     //??(非null,var!=null) ?!(非空字符串, Utils.isNoteEmpty(var))  
        //例:m.icon??  ::m.icon!=null  
        //例:m.icon?!  ::Utils.isNoteEmpty(m.icon)  

for 循环控制指令 (通过 ${var}_index 可获得序号,例:m_index)  
  var (属性:循环变量申明)  
  items (属性:集合变量名称)  
  sep? (属性:分隔符)  

trim 修剪指令  
  trimStart(属性:开始位去除)  
  trimEnd(属性:结尾位去除)  
  prefix(属性:添加前缀)  
  suffix(属性:添加后缀)  

ref 引用代码块指令  
  sql (属性:代码块id)  

name:type    = 变量申明(可用于属性::param, :declare,var,或宏定义 @{..},${..})  
@{name:type?} = 变量注入  
${name:type?} = 变量替换,即成为字符串的一部份  

//列表([]替代<>)  
return="List[weed3demo.mapper.UserModel]" => List<UserModel>  
return="List[String]" => List<String> (Date,Long,...大写开头的单值类型)  
return="MapList" => List<Map<String,Object>>  
return="DataList" => DataList  

//一行  
return="weed3demo.mapper.UserModel" => UserModel  
return="Map" => Map<String,Object>  
return="DataItem" => DataItem  

//单值  
return="String" => String (任何单职类型)

Четыре. Table 语法

(一) 条件操作 (与Mapper共享)

方法 效果说明
where, whereIf
whereEq, whereNeq ==, !=
whereLt, whereLte <, <=
whereGt, whereGte >, >=
whereLk, whereNlk LIKE, NOT LIKE
whereIn, whereNin IN(..), NOT IN(..)
whereBtw, whereNbtw BETWEEN, NOT BETWEEN
and系统方法 同where
or系统方法 同where
begin (
end )

(二) 表操作 (Table独占)

方法 效果说明
set, setIf 设置值
setMap, setMapIf 设置值
setEntity, setEntityIf 设置值
table 主表
innerJoin, leftJoin, rightJoin 关联表
on, onEq 关联条件
orderBy, orderByAsc, orderByDesc 排序
groupBy
having 组条件
limit 限制范围
select 查询(返回IQuery)
count 查询快捷版,统计数量
exists 查询快捷版,是否存在
update 更新
insert 插入
delete 删除

(三) IQuery接口

  • long getCount() throws SQLException;
  • Object getValue() throws SQLException;
  • <T> T getValue(T def) throws SQLException;
  • Variate getVariate() throws SQLException;
  • <T> T getItem(Class<T> cls) throws SQLException;
  • <T> List<T> getList(Class<T> cls) throws SQLException;
  • DataList getDataList() throws SQLException;
  • DataItem getDataItem() throws SQLException;
  • List<Map<String,Object>> getMapList() throws SQLException;
  • Map<String,Object> getMap() throws SQLException;
  • <T> List<T> getArray(String column) throws SQLException;
  • <T> List<T> getArray(int columnIndex)

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

  • Кэш (можно пропустить)
ICacheServiceEx cache = new LocalCache().nameSet("cache");

User user = db.table("user")
              .whereEq("id",12)
              .caching(cache)  // Кэширование, время по умолчанию для кэша
              .selectItem("*", User.class);
  • Контроль кэша (можно пропустить)
// При запросе кеш
User user = db.table("user")
              .whereGt("id", 12)
              .limit(100, 20) // Пейджинговый запрос
              .caching(cache)
              .usingCache(60 * 5)     // Кэшировать на 5 минут
              .cacheTag("user_all") // Добавить метку кэша user_all
              .selectList("*", User.class);

// Обновить и очистить кэш // При следующем запросе можно получить последние данные
db.table("user").set("sex", 0).whereEq("id", 101).update();
cache.clear("user_all");
  • Транзакции в одной базе данных
Trans.tran(()->{
  // Регистрация пользователя
  long user_id = userDao.addUser(user);
  
  // После регистрации отправить 10 золотых монет (завершить в той же транзакции)
  userDao.addUserGold(user_id, 10);
});
  • Распределённые транзакции
Trans.tran(() -> {
    // Система пользователей, добавление пользователя и золотых монет
    user.id = userDao.addUser(user); // id увеличивается автоматически

    // Банковская система
    bankDao.addAccount(user.id); // Создание учётной записи
    bankDao.addAccountGold(user.id, 10); // Добавление золотых монет на счёт
    bankDao.addJournal(user.id,10); // Добавить запись в журнал
  
    // Распространение сообщений // Для последующего горизонтального расширения бизнеса
    MsgUtil.sendMessage("user.registered",user.value);
});

Шесть. Мониторинг и запись

  • Мониторинг исключений
WeedConfig.onException((cmd,ex)->{
  // Можно сделать запись
    ex.printStackTrace();
});
  • Наблюдение за производительностью
WeedConfig.onExecuteAft((cmd)->{
  //cmd.timespan()  // Получить время выполнения (в миллисекундах)
});
  • Запись действий
WeedConfig.onLog((cmd) -> {
    if (cmd.isLog >= 0) { //isLog: -1, не нужно записывать; 0, по умолчанию; 1, нужно записать
        //cmd.text;         // Выполняемый код
        //cmd.paramS;         // Параметры выполнения
        //cmd.paramMap();   // Параметры выполнения в виде карты
    }
});
  • Фильтрация кода
// Пример: запретить операцию DELETE
WeedConfig.onExecuteBef((cmd)->{
    if(cmd.text.indexOf("DELETE ") >=0){
        return false;
    }
    return true;
});

Семь. Встраивание в JVM-скрипты

  • Встраивание в движок JavaScript (Nashorn)
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine _eng = scriptEngineManager.getEngineByName("nashorn");
Invocable _eng_call = (Invocable)_eng;
_eng.put("db", db);

/*
 * var map = db.table("user").where('id=?',1).getMap();
 * var user_id = map.id;
 */
  • Встраивание в Groovy-движок
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine _eng = scriptEngineManager.getEngineByName("groovy");
Invocable _eng_call = (Invocable)_eng;
_eng.put("db", db);

/*
 * def map = db.table("user").where('id=?',1).getMap();
 * def user_id = map.id;
 */

Восемь. Синтаксис

1
https://api.gitlife.ru/oschina-mirror/noear-weed3.git
git@api.gitlife.ru:oschina-mirror/noear-weed3.git
oschina-mirror
noear-weed3
noear-weed3
master