MiraiForum

    • 注册
    • 登录
    • 搜索
    • 热门
    • 最新
    • 未解决
    • 标签
    • 群组
    • 友情链接

    mirai-console 插件使用 Hibernate-JPA 的 方式 调用数据库

    技术交流板块
    3
    12
    764
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • cssxsh
      cssxsh 最后由 cssxsh 编辑

      JPA是Java Persistence API的简称,中文名Java持久层API,
      可以通过 注解或XML描述对象-关系表的映射关系。

      简单来说,你声明了一个实体类,可以通过注解的方式,实现

      • 将注解类和数据库中是数据表关联,一一对应
      • 将实体类的属性和数据列关联,一一对应
      • 将实体类和实体类关联,外键

      以上功能 sql 将会自动生成,而不需要手写 sql 。

      jpa 这种方式有利有弊。相对于 mybatis 来说,隐去了维护 sql 的麻烦,方便支持多数据库平台,
      缺点是性能差,不利于像 mybatis 一样可以通过插件拓展功能。
      但是一般来说,mirai-console 插件 并不需要过多关注数据库性能。

      可作为前置插件的成品,带有一个消息记录器持久化消息到数据库
      https://github.com/cssxsh/mirai-hibernate-plugin

      首先在 dependencies 中加入相关依赖
      从我的前置插件中获得

      dependencies {
          implementation("xyz.cssxsh.mirai:mirai-hibernate-plugin:2.4.4")
      }
      
      mirai {
          jvmTarget = JavaVersion.VERSION_11
      }
      

      或者你可以从原始库中获得

      
      dependencies {
      
          // SQL/ORM 
      
          api("org.hibernate.orm:hibernate-core:6.1.3.Final")
      
          api("org.hibernate.orm:hibernate-hikaricp:6.1.3.Final")
      
          api("org.hibernate.orm:hibernate-community-dialects:6.1.3.Final")
      
          // 连接池
      
          api("com.zaxxer:HikariCP:5.0.1")
      
          // 数据库驱动
      
          api("com.h2database:h2:2.1.214")
      
          api("org.xerial:sqlite-jdbc:3.39.3.0")
      
          api("mysql:mysql-connector-java:8.0.30")
      
          api("org.postgresql:postgresql:42.5.0")  
      
      
      
          testImplementation(kotlin("test"))
      
          testImplementation("org.slf4j:slf4j-simple:2.0.0")
      
          testImplementation("net.mamoe:mirai-logging-slf4j:2.12.3")
      
          testImplementation("net.mamoe:mirai-core-utils:2.12.3")
      
      }
      
      
      
      mirai {
      
          jvmTarget = JavaVersion.VERSION_11
      
      }
      
      
      1 条回复 最后回复 回复 引用 1
      • cssxsh
        cssxsh 最后由 cssxsh 编辑

        然后初始化 hibernate 配置
        org.hibernate.cfg.Configuration

        这里,为了方便用户修改,我们使用从插件数据目录加载文件配置的方式 data/xxxxx/hibernate.properties
        通过修改以下几个配置项可以调整插件使用的数据库

        hibernate.connection.url
        hibernate.connection.driver_class
        hibernate.dialect
        

        你可以使用我的前置插件中提供的 MiraiHibernateConfiguration
        MiraiHibernateConfiguration 会自动扫描 entry, entity, entities, model, models, bean, beans, dto 包中的类
        但只会扫描一个包,先发现那个包就扫那个包
        并且会自动生成 h2 数据库 的 hibernate.properties 文件
        java

            var configuration = new MiraiHibernateConfiguration(this);
        

        kotlin

            val configuration = MiraiHibernateConfiguration(plugin = this)
        

        hibernate.properties 文件内容示例

        hibernate.connection.url=jdbc:h2:file:./data/xxxxx/hibernate.properties
        hibernate.connection.driver_class=org.h2.Driver
        hibernate.dialect=org.hibernate.dialect.H2Dialect
        hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider
        hibernate.hikari.connectionTimeout=180000
        hibernate.connection.isolation=1
        hibernate.hbm2ddl.auto=update
        hibernate-connection-autocommit=true
        hibernate.connection.show_sql=false
        hibernate.autoReconnect=true
        

        或者你可以使用原生的加载方式

        java

        
            @Override
        
            public void onEnable() {
        
                Configuration configuration = new Configuration();
        
               // 加载用户配置
        
                try (InputStream in = new FileInputStream(resolveDataFile("hibernate.properties"))) {
        
                    configuration.getProperties().load(in);
        
                } catch (Exception e) {
        
                    getLogger().error(e);
        
                }
        
                // 添加 实体类
        
                configuration.addAnnotatedClass(User.class);
        
            }
        
        

        kotlin

        
            override fun onEnable() {
        
                val configuration = Configuration()
        
               // 加载用户配置
        
                resolveDataFile("hibernate.properties").inputStream()
        
                    .use { `in` -> configuration.properties.load(`in`) }
        
                // 添加 实体类
        
                configuration.addAnnotatedClass(User::class.java)
        
            }
        
        
        1 条回复 最后回复 回复 引用 0
        • cssxsh
          cssxsh 最后由 编辑

          实体类的示例

          java

          @Entity
          @Table(name = "user")
          public class User {
              @Id
              private Long id;
          
              private String name;
          
              @OneToMany(mappedBy = "user")
              private List<Work> works;
          
              public void setId(Long id) {
                  this.id = id;
              }
          
              public Long getId() {
                  return id;
              }
          
              public void setName(String name) {
                  this.name = name;
              }
          
              public String getName() {
                  return name;
              }
              public List<Work> getWorks() {
                  return works;
              }
          
              public void setWorks(List<Work> works) { this.works = works;}
          }
          
          @Entity
          @Table(name = "work")
          public class Work {
              @Id
              private Long pid;
              private String content;
              @ManyToOne
              private User user;
          
              public Long getPid() {
                  return pid;
              }
          
              public void setPid(Long pid) {
                  this.pid = pid;
              }
          
              public String getContent() {
                  return content;
              }
          
              public void setContent(String content) {
                  this.content = content;
              }
          
              public User getUser() {
                  return user;
              }
          
              public void setUser(User user) {
                  this.user = user;
              }
          }
          

          kotlin

          @Entity
          @Table(name = "user")
          public data class User(
              val id: Long,
              val name: String,
              @OneToMany(mappedBy = "user")
              val works: List<Work>
          )
          
          @Entity
          @Table(name = "user")
          public data class Work(
              val pid: Long,
              val content: String,
              @ManyToOne
              val user: User
          )
          
          1 条回复 最后回复 回复 引用 0
          • cssxsh
            cssxsh 最后由 cssxsh 编辑

            创建一个 SessionFactory 并使用

            注意,对于一个插件来说,factory 应该在 onEnable 中创建,onDisable 中关闭。并且不对一个数据库创建多个 factory。

            java

                   var factory = configuration.buildSessionFactory();
            
                    // 用这个方法能安全关闭 Session 不需要额外写 try catch
                    var u = factory.fromSession((session) -> {
                        // 查找
                        return session.find(User.class, 1L);
                    });
                    // 用这个方法能安全关闭 Transaction 不需要额外写 try catch
                    // fromTransaction 在 fromSession 的基础上套了一层 事务(Transaction)
                    // 当有数据变更时,必须套着事务
                    factory.fromTransaction((session) -> {
                        // 插入
                        var user = new User();
                        user.setId(1L);
                        user.setName("name");
                        session.persist(user);
            
                        // 修改
                        user.setName("new name");
                        session.merge(user);
            
                        // 删除
                        session.remove(user);
            
                        // 原生sql 查询
                        session.createNativeQuery("select * from user", User.class)
                                .list();
            
                        // hql 查询(格式像是 sql 混杂 java)
                        String hql = "from User s where s.name = :name";
                        session.createQuery(hql, User.class)
                                .setParameter("name", "...")
                                .list();
            
                        String hql2 = "from Work w where w.user.name = :name";
                        session.createQuery(hql2, Work.class)
                                .setParameter("name", "...")
                                .list();
            
                        // criteria 查询(纯 java 代码的方式构造 sql)我推荐这种,不过用起来比较复杂
                        var builder = session.getCriteriaBuilder();
            
                        var query = builder.createQuery(User.class);
                        var root = query.from(User.class);
                        query.select(root);
                        query.where(builder.between(root.get("id"), 0L, 1000L));
            
                        var list = session.createQuery(query)
                                .list();
            
                        var query2 = builder.createQuery(Work.class);
                        var root2 = query.from(Work.class);
                        query2.select(root2);
                        query2.where(builder.between(root2.get("user").get("id"), 0L, 1000L));
            
                        var list2 = session.createQuery(query)
                                .list();
            
                        return 0;
                    });
            
            1 条回复 最后回复 回复 引用 0
            • cssxsh
              cssxsh 最后由 cssxsh 编辑

              顺带一提
              mirai-hibernate-plugin 中提供了消息记录器 xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder, 并且已经注册到mirai-hibernate-plugin,可以直接使用

              MiraiHibernateRecorder 提供多个 get 函数 ,可以使用
              MessageSource. Bot, Member 等作为参数 获得历史消息

              例子
              kotlin

                  // 返回一个流,请记得关闭这个流
                  MiraiHibernateRecorder[subject].use { steam ->
                      steam.forEach { record ->
                          // 转化成消息链
                          record.toMessageChain()
                          // 转化消息引用
                          // 这里的 originalMessage 来自 上面的 toMessageChain
                          record.toMessageSource().originalMessage
                      }
                  }
                  // 返回一个列表,第 2,3 参数是 开始时刻和结束时间
                  MiraiHibernateRecorder[subject, 16000000, 160000000].forEach { record ->
                      // 转化成消息链
                      record.toMessageChain()
                      // 转化消息引用
                      // 这里的 originalMessage 来自 上面的 toMessageChain
                      record.toMessageSource().originalMessage
                  }
              }
              

              java

                      // 返回一个流,请记得关闭这个流
                      try (var steam = MiraiHibernateRecorder.INSTANCE.get(Bot.findInstance(123456))) {
                          MessageRecord record = steam.findFirst().get();
                          // 转化成消息链
                          MessageChain message = record.toMessageChain();
                          // 转化消息引用
                          // 这里的 originalMessage 来自 上面的 toMessageChain
                          MessageSource source = record.toMessageSource();
                          
                      } catch (Exception e) {
                          //
                      }
                      // 返回一个列表,第 2,3 参数是 开始时刻和结束时间
                      var list = MiraiHibernateRecorder.INSTANCE.get(Bot.getInstance(123456), 1600000, 1600001);
                      for (MessageRecord record : list) {
                          record.toMessageSource();
                          // or
                          record.toMessageChain();
                      }
              
              1 条回复 最后回复 回复 引用 0
              • SekiBetu
                SekiBetu 最后由 编辑

                接触JPA三个过程:
                初识,诶,挺好用的,不用写sql语句了,
                深入使用,嗯?怎么我这种复杂的的动态查询sql语句没办法实现,
                结局,回归原始mybatis

                cssxsh 1 条回复 最后回复 回复 引用 0
                • cssxsh
                  cssxsh @SekiBetu 最后由 编辑

                  @SekiBetu

                  一般来说是可以的
                  只不过会因为和实体类直接绑定显得很蹩脚?

                  1 条回复 最后回复 回复 引用 0
                  • B
                    baofengqwq 最后由 编辑

                    2023-01-17-162104_1024x768_scrot.png
                    @cssxsh 大佬,我想问下,为啥装上插件后会显示disabled,需要怎么开启呢

                    cssxsh 1 条回复 最后回复 回复 引用 0
                    • cssxsh
                      cssxsh @baofengqwq 最后由 编辑

                      @baofengqwq

                      因为插件初始化失败
                      你应该检查最开始部分的日志
                      看看有没有问题

                      另外日志文件在 logs

                      B 1 条回复 最后回复 回复 引用 0
                      • B
                        baofengqwq @cssxsh 最后由 编辑

                        @cssxsh 大佬,这日志我也看不懂啊,找不到问题

                        cssxsh 1 条回复 最后回复 回复 引用 0
                        • cssxsh
                          cssxsh @baofengqwq 最后由 编辑

                          @baofengqwq

                          ?

                          B 1 条回复 最后回复 回复 引用 0
                          • B
                            baofengqwq @cssxsh 最后由 编辑

                            @cssxsh 没事了,我重新安装了插件就不会disabled了

                            1 条回复 最后回复 回复 引用 0
                            • 1 / 1
                            • First post
                              Last post
                            Powered by Mamoe Technologies & NodeBB | 友情链接 | 服务监控 | Contact