8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

将我的 private.key 文件传递​​给 Spring Boot Hibernate 中的 @ColumnTransformer

Essex Boy 2月前

53 0

我已将 private.key 和 public.key 存储在资源文件夹中,并使用 public static String loadPrivateKey() throws IOException { Resource privateKeyResource = new

我已将 private.key 和 public.key 存储在资源文件夹中,并使用

`    public static String loadPrivateKey() throws IOException {
        Resource privateKeyResource = new ClassPathResource("private.key");
        return readInputStream(privateKeyResource.getInputStream());
    }
    private static String readInputStream(InputStream inputStream) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        return stringBuilder.toString();`

我已经使用它进行了测试 Systemout 并且 Keyloader 运行正常。

但是当我尝试将其注入@ColumnTransformer时,它会出现错误 java: element value must be a constant expression .

下面是我的实体类中与此相关的实现部分。

`    private static final String PUBLIC_KEY;
    private static final String PRIVATE_KEY;

    static {
        try {
            PUBLIC_KEY = KeyLoader.loadPublicKey();
            PRIVATE_KEY = KeyLoader.loadPrivateKey();
        } catch (IOException e) {
            throw new RuntimeException("Failed to load keys", e);
        }
    }

    private static final String readEx="pgp_pub_decrypt(asymdata, dearmor('" + PRIVATE_KEY + "'),'passphrase')";
    private static final String writeEx= "pgp_pub_encrypt(?, dearmor('"+PUBLIC_KEY+"'))";


    @Column(name="asymdata", columnDefinition = "bytea")
    @ColumnTransformer(
            read = readEx,
            write = writeEx)
    private String asymdata;`

注意:在这里我使用 pg-crypto 使用非对称密钥(公钥)加密来加密 postgreSQL 数据库中的数据。

我需要的是一种从 private.key 文件中获取我的私钥并将其传递给 ColumnTransformer 注释的方法。

帖子版权声明 1、本帖标题:将我的 private.key 文件传递​​给 Spring Boot Hibernate 中的 @ColumnTransformer
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Essex Boy在本站《hibernate》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 从 Quarkus 2 升级到 Quarkus 3 后,panache 查询停止工作。示例:@Entitypublic class Cat extends Serializable { @Id @Column(unique = true, nullable = false) prot...

    从 Quarkus 2 升级到 Quarkus 3 后,panache 查询停止工作。

    例子:

    @Entity
    public class Cat extends Serializable {
    
        @Id
        @Column(unique = true, nullable = false)
        protected UUID id;
    
        private LocalDate electionDate;
    }
    
    @ApplicationScoped
    public class CatRepository implements PanacheRepository<Cat> {
       public Uni<Cat> findByElectionDate(){
           return find("electionDate", '2024-01-07').firstResult();
       }
    }
    

    panache 查询:\'electionDate = '2024-01-07'\'。

    发生错误:\' Caused by: org.hibernate.query.SemanticException: Cannot compare left expression of type 'java.time.LocalDate' with right expression of type 'java.lang.String'"

    Quarkus 第二版没有出现任何问题。

    Panache 请求:\'electionDate = {d'2024-01-07'}\'它可以工作,但需要大量成本来处理前端.................................................................

  • ZR07 2月前 0 只看Ta
    引用 2

    在一个项目中,我们使用 Querydsl 创建动态查询并应用排序和分页。当以下对象中存在一对多和多对一关系时,以下代码非常慢

    在一个项目中,我们使用 Querydsl 创建动态查询并应用排序和分页。当以下对象 ticket、payment 和 ticket_payment 中存在 oneTomany 和 manytoone 关系时,以下代码非常慢。

    特别是在此部分应用分页时,需要 10 多分钟才能返回 10 个项目。

    List<?> result = querydsl.applyPagination(pageable, query).fetch();
    

    可分页的值为 offset=0、size=10,并且有用于排序的值。

    在生成的查询中没有添加“获取第一个?仅获取行”部分,所以我猜想它花费的时间太长,因为它加载了 ticket 和 ticket_payment 表?Ticket 表有 100K 行,Ticket_payment 有 120k。在 sql developer 中运行相同的查询时,返回 50 行只需不到 4 秒。

    为了更快地获得结果,可以进行哪些改进?是否可以定制 fetch() 方法以使其运行得更快?

    QueryDSL 获取代码:

    org.springframework.data.domain.PageImpl;
    org.springframework.data.domain.Pageable;
    
    Querydsl querydsl = new Querydsl(entityManager, (new PathBuilderFactory()).create(<EntityClass>.class));
    JPQLQuery<?> query = new JPAQuery<>(entityManager);
    
    //TODO: prepare your query here 
    
    //Get the count
    Long totalElements = query.fetchCount();
    
    //Apply the pagination
    List<?> result = querydsl.applyPagination(pageable, query).fetch();
    //Returns 10 items but takes more than 10min
    
    //return a paged response
    return new PageImpl<>(result, pageable, totalElements);
    

    以及以下实体和表格

        @Entity
        @Table(name = "TICKET")
        public class Ticket implements Serializable {
        
            @Id
            @Column(name = "TICKET_ID", nullable = false)
            private Long id;
        
            @OneToMany(mappedBy = "Ticket")
            private Set<TicketPayment> ticketPayments= new HashSet<>();
        ...
        }
    

    支付

    @Entity
    @Table(name = "PAYMENT")
    public class Payment implements Serializable {
    
    @Id()
    @Column(name = "PAYMENT_ID")
    private Long id;
    @Column(name = "PAYMENT_CODE")
    private String code;
    

    连接表

    @Entity
    @Table(name = "TICKET_PAYMENT")
    @IdClass(TicketPaymentPK.class)
    public class TikcetPayment{
    
    
    @Id
    @Column(name = "TICKET_ID", nullable = false, precision = 0)
    private long ticketId;
    
    @Id
    @Column(name = "PAYMENT_ID", nullable = false, precision = 0)
    private long paymentId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TICKET_ID", referencedColumnName = "TICKET_ID", nullable = false, insertable = false, updatable = false)
    private Ticket ticket;
    
    @ManyToOne
    @JoinColumn(name = "PAYMENT_ID", referencedColumnName = "PAYMENT_ID", nullable = false, insertable = false, updatable = false)
    private Payment payment;
    ...
    

    复合键

    public class TicketPaymentPKimplements Serializable {
    @Id
    @Column(name = "TICKET_ID", nullable = false, precision = 0)
    private long ticketId;
    @Id
    @Column(name = "PAYMENT_ID", nullable = false, precision = 0)
    private long paymentId;
    
  • 迁移到 Hibernate 6 后,调用 HibernateDaoSupport getSessionFactory()、HibernateDaoSupport setSessionFactory() 失败,错误原因为:java.lang.NoClassDefFoundError:org/hiber...

    迁移到 Hibernate 6 后,对 HibernateDaoSupport getSessionFactory()、HibernateDaoSupport setSessionFactory() 的调用失败并出现错误

    导致原因:java.lang.NoClassDefFoundError:org/hibernate/criterion/Criterion atdeployment.SLISEAR-0.0.1-SNAPSHOT.ear.SLIS-0.0.1-SNAPSHOT.war//org.springframework.orm.hibernate5.support.HibernateDaoSupport.createHibernateTemplate(HibernateDaoSupport.java:84)atdeployment.SLISEAR-0.0.1-SNAPSHOT.ear.SLIS-0.0.1-SNAPSHOT.war//org.springframework.orm.hibernate5.support.HibernateDaoSupport.setSessionFactory(HibernateDaoSupport.java:70)at

    那么在Hibernate 6中使用什么类和方法来替代HibernateDaoSupport.getSessionFactory(),setSessionFactory()呢?

  • 在这里,我尝试使用 JPA 设置布尔值,但返回的字段名称和值都与我实际传递的不同。如果你看我的请求,我已经传递了 isCouponApli...

    在这里,我尝试使用 JPA 设置布尔值,但返回的字段名称和值都与我实际传递的不同。如果你看看我的请求,我已经通过了, isCouponAplied 但作为回报,我得到的只是 couponAplied .

    package com.example.curd_example.model;
    
    import java.io.Serializable;
    
    import jakarta.persistence.CascadeType;
    import jakarta.persistence.Entity;
    import jakarta.persistence.GeneratedValue;
    import jakarta.persistence.GenerationType;
    import jakarta.persistence.Id;
    import jakarta.persistence.JoinColumn;
    import jakarta.persistence.OneToOne;
    import jakarta.persistence.Table;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    @Entity
    @Table(name = "OrderDetails")
    @Getter
    @Setter
    public class OrderDetails implements Serializable{
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        private String name;
        @OneToOne(cascade = CascadeType.ALL)
        @JoinColumn(name = "Order_detail_id", referencedColumnName = "id")
        private ItemDetails itemDetails;
        private int quantity;
        private double price;
        private double discountedAmount;
        private double discountPercent;
        private boolean isCouponAplied;
        private double discountedAmountForCoupon;
        private double totalSavings;
    }
    
    

    要求:

    {
        "name": "Sankar",
        "itemDetails":{
            "quantity":1,
            "itemName": "DOSA"
        },
        "quantity": 2,
        "price": 39.98,
        "discountedAmount": 10,
        "discountPercent": 32.27,
        "isCouponAplied": true,
        "discountedAmountForCoupon": 10,
        "totalSavings": 20
    }
    

    回复:

    {
        "id": 2,
        "name": "Sankar",
        "itemDetails": {
            "id": 2,
            "quantity": 1,
            "itemName": "DOSA"
        },
        "quantity": 2,
        "price": 39.98,
        "discountedAmount": 10.0,
        "discountPercent": 32.27,
        "discountedAmountForCoupon": 10.0,
        "totalSavings": 20.0,
        "couponAplied": false
    }
    

    数据库中的数据

    它应该存储我实际传递的值。

  • 我想要重新创建的案例:应用程序启动并将一个表从 postgres DB 放入缓存中必须执行对该表的所有查询,并使用二级缓存而不是 DB 连接。build.gradle:

    我想重新创建的情况:

    1. 应用程序启动并将一个表从 postgres DB 放入缓存
    2. 必须执行对此表的所有查询并使用二级缓存而不是数据库连接。

    构建.gradle:

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.hibernate.orm:hibernate-jcache:6.1.6.Final'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        runtimeOnly 'org.postgresql:postgresql'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        implementation 'org.ehcache:ehcache:3.10.8'
    }
    

    应用程序.属性:

    spring.jpa.show-sql=true
    spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
    spring.jpa.properties.hibernate.format_sql=true
    spring.jpa.properties.hibernate.generate_statistics=true
    spring.jpa.properties.hibernate.cache.use_second_level_cache=true
    spring.jpa.properties.hibernate.cache.use_query_cache=true
    spring.jpa.properties.hibernate.cache.region.factory_class= 
    org.hibernate.cache.jcache.JCacheRegionFactory
    spring.jpa.hibernate.ddl-auto=none
    

    缓存初始化:

    @Configuration
    public class CacheConfig {
    
        private final RegionsRepo repo;
    
        public CacheConfig(RegionsRepo repo) {
            this.repo = repo;
        }
    
        @PostConstruct
        public void initCache() {
            repo.findAll().forEach(region -> {});
        }
    }
    

    实体注释:

    @Entity
    @Cacheable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) 
    @Table(name = "regions")
    

    和JpaRepository:

    public interface RegionsRepo extends JpaRepository<Regions, Long> {
        
        Optional<Regions> findRegionsByExternalid(Integer id);
    
        @Override
        Optional<Regions> findById(Long aLong);
    
        @Override
        @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
        List<Regions> findAll();
    }
    

    启动后,应用程序将表放入二级休眠缓存。日志:

     346333 nanoseconds spent acquiring 1 JDBC connections;
        0 nanoseconds spent releasing 0 JDBC connections;
        73250 nanoseconds spent preparing 1 JDBC statements;
        1098084 nanoseconds spent executing 1 JDBC statements;
        0 nanoseconds spent executing 0 JDBC batches;
        5962251 nanoseconds spent performing 3 L2C puts;
        0 nanoseconds spent performing 0 L2C hits;
        368667 nanoseconds spent performing 1 L2C misses;
        0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
        68458 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
    

    当我调用存储库方法时

    查找 ID

    它是从 JpaRepository 覆盖的,我发现没有执行 JDBC 语句,只有纯粹的 L2C 命中,这正是我需要的。日志:

        1277291 nanoseconds spent acquiring 1 JDBC connections;
        0 nanoseconds spent releasing 0 JDBC connections;
        0 nanoseconds spent preparing 0 JDBC statements;
        0 nanoseconds spent executing 0 JDBC statements;
        0 nanoseconds spent executing 0 JDBC batches;
        0 nanoseconds spent performing 0 L2C puts;
        418709 nanoseconds spent performing 1 L2C hits;
        0 nanoseconds spent performing 0 L2C misses;
        0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
        0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
    

    但是当我使用自定义而不是覆盖的方法时

    通过外部 ID 查找区域

    L2C 命中被忽略。日志:

    742083 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    849083 nanoseconds spent preparing 1 JDBC statements;
    2029042 nanoseconds spent executing 1 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    670583 nanoseconds spent performing 1 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
    

    在这种情况下,您能告诉我 Hibernate 缓存配置有什么问题吗?

    我应该怎么做才能让 Hibernate 在非覆盖的 JpaRepository 方法中使用 L2C 缓存?

  • 这是查询 DSL 中的一个已知问题,它会在不应该调用计数时调用计数。我解决这个问题的方法是创建一个自定义存储库并实现 findAll(Predicate predicate, Pageable pageable) 不带计数的:

    public interface MyCustomRepository<T, I> extends Repository<T, I> {
    
        Page<T> findAll(Predicate predicate, Pageable pageable);
    
    }
    
    public class MyCustomRepositoryImpl<T, I extends Serializable> extends QuerydslJpaPredicateExecutor<T> implements MyCustomRepository<T, I> {
    
        private final EntityPath<T> path;
        private final Querydsl querydsl;
        private final EntityManager em;
        private final Class<T> entityType;
    
        public GenericRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation, final EntityManager entityManager) {
            super(entityInformation, entityManager, SimpleEntityPathResolver.INSTANCE, null);
            this.em = entityManager;
            this.entityType = entityInformation.getJavaType();
            // get the fields from the base class rather than instantiate them
            this.path = Fields.getIgnoreAccess(this, Fields.getDeclaredField(QuerydslJpaPredicateExecutor.class, "path"));
            this.querydsl = Fields.getIgnoreAccess(this, Fields.getDeclaredField(QuerydslJpaPredicateExecutor.class, "querydsl"));
        }
    
        public Page<T> findAll(Predicate predicate, Pageable pageable) {
            JPQLQuery<T> jpqlQuery = (JPQLQuery<T>) createQuery(predicate);
            jpqlQuery = querydsl.applySorting(pageable.getSort(), jpqlQuery)
                    .offset(pageable.getOffset())
                    .limit(pageable.getPageSize() + 1L);
    
            // we fetch one extra entity to correcly calculate the page
            List<P> result = jpqlQuery.fetch();
    
            int size = result.size();
            if (size > pageable.getPageSize()) {
                result.remove(size - 1);
            }
            return new PageImpl<>(result, pageable, pageable.getOffset() + size);     
        }
    }
    

    要使用这个自定义存储库,您还需要:

    public class CustomRepositoryFactoryBean<T extends Repository<S, I>, S, I>
            extends JpaRepositoryFactoryBean<T, S, I> {
    
        public CustomRepositoryFactoryBean(final Class<? extends T> repositoryInterface) {
            super(repositoryInterface);
        }
    
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
            return new CustomRepositoryFactory(entityManager);
        }
    }
    

    public class CustomRepositoryFactory extends JpaRepositoryFactory {
    
        private final EntityManager entityManager;
    
        public CustomRepositoryFactory(final EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }
    
        @Override
        protected RepositoryFragments getRepositoryFragments(final RepositoryMetadata metadata) {
            RepositoryFragments fragments = super.getRepositoryFragments(metadata);
    
            if (MyCustomeRepository.class.isAssignableFrom(metadata.getRepositoryInterface())) {
                JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
    
                Object queryableFragment = instantiateClass(MyCustomRepositoryImpl.class, entityInformation, entityManager);
    
                RepositoryFragments newFragments = RepositoryFragments.of(RepositoryFragment.implemented(queryableFragment));
                fragments = newFragments.append(fragments);
            }
    
            return fragments;
        }
    }
    

    然后在 @EnableJpaRepositories 注释中必须指定:

    @EnableJpaRepositories(
            repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class,
            ...)
    

    并确保所有想要使用此功能的存储库都需要实现接口, MyCustomRepository 而不是 QuerydslJpaPredicateExecutor .

    注意:和 Fields.getIgnoreAccess 只是 Fields.getDeclaredField 一些实用方法,它们通过反射从基类返回这 2 个字段,因为它们是私有的,并且不需要重新初始化它们以占用更多内存,请随意实现它们。

  • 使用 Lombok Getter/Setter 作为布尔值将生成 isXXXX getter 方法 引用 ,因此布尔字段应该重命名 couponApplied .

  • @legendofawesomeness lombok 创建 getter 方法,而不是 JPA。

  • 我怀疑这可能是因为 JPA 默认为布尔字段创建了一个 \'isXXX\' getter 方法,并假定字段名称为 \'XXX\'。尝试将字段重命名为 \'couponApplied\'。

  • 最后还是自己解决了。

    由于 hibernate-jache 默认提供程序可以通过作为主键的实体 id 进行搜索,因此我只需在数据库中重新创建没有 id 的实体即可。

    签名:

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    Integer externalid;
    String externalname;
    Integer internalid;
    String internalname;
    

    这里我们可以使用 Long id 字段在 L2C 中进行扫描和搜索。

    到:

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    Integer externalid;
    String externalname;
    Integer internalid;
    String internalname;
    

    现在 Long id 有点等于 externalid,我可以在 L2C 缓存中通过它进行搜索。

返回
作者最近主题: