从数据库中检索到的实体与查询中的情况相同

     2023-03-24     262

关键词:

【中文标题】从数据库中检索到的实体与查询中的情况相同【英文标题】:Entity retrieved from database with same case as in query 【发布时间】:2017-10-25 06:08:23 【问题描述】:

我的数据库包含下表:

表:

country 
    code varchar(255) not null
        primary key
;

类:

@Entity
public class Country 
    @Id
    @Column(name = "code")
    private String mCode;

    public String getCode() 
        return mCode;
    

    public void setCode(String code) 
        mCode = code;
    

示例表行:

| code |
|------|
| USA  |
| UK   |

当我使用以下 CrudRepository 检索国家/地区时:

public interface CountryRepository extends CrudRepository<Country, String> 

第一个场景:

mRepository.findOne("USA")

它会在我的 rest api 中给我以下结果:


  "code": "USA"

第二种情况:

mRepository.findOne("UsA")

它会在我的 rest api 中给我以下结果:


  "code": "UsA"

第三种情况:

mRepository.findOne("Usa")

它会在我的 rest api 中给我以下结果:


  "code": "Usa"

我还使用调试器检查了相同的行为,发现我在内存中的对象实际上具有相同的行为。

我想要的:我希望返回的数据与数据库中的大小写相同。

【问题讨论】:

salaam,您如何返回/转换发送回客户端的数据? return mRepository.findOne("UsA") 我的意思是从存储库返回的对象如何转换为前端接收的 json。 Java --> JSON? 我认为 Spring 默认使用 Jackson。但是这种行为发生在转换之前。在检索数据库行之后和返回对象之前,我使用调试器进行了检查。 您使用的是 MySQL,对吗?请确保,您在表格列上使用了正确的字符集。名为 *_ci 的字符集不区分大小写,这会导致此问题。见dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html 【参考方案1】:

正如@Bedla 在评论中已经暗示的那样,您可能在数据库中使用不区分大小写的 varchar 数据类型。但是,不建议对 Hibernate 中的主键(一般情况下)这样做,因为 Hibernate 在引用持久性上下文中的实体时依赖 id 属性值的唯一性(如果启用,则为二级缓存)。

例如,在通过"USA""usa" 加载实体之后(或者在合并一个分离的"usa""USA" 已经加载之后等)在相同的持久化上下文中,您最终可能会得到持久化上下文中的两个不同实例,这进一步意味着它们将被分别刷新,从而覆盖彼此的更改,等等。

在数据库中使用区分大小写的数据类型,但允许忽略大小写进行搜索:

public interface CountryRepository extends CrudRepository<Country, String> 
    @Query("select c from Country c where upper(c.mCode) = upper(?1)")
    Country getCountry(String code);

PS 国家/地区代码通常不适合用作主键,因为它们可以更改。

【讨论】:

Country getByMCodeIgnoreCase(String code) 将给出相同的结果而没有@Query + IMO,您应该重写CrudRepository.findOne 方法以引发异常,因此开发人员只能使用正确的方法来获取实体。跨度> 【参考方案2】:

尝试覆盖您的实体中的equals()hashcode() 方法,以便它们考虑mCase 的情况(并且不要在这两种方法中使用任何其他字段)。

【讨论】:

【参考方案3】:

您可以将 CrudRepository 包装在另一个层,例如服务或控制器。

public Country findOne(String code)
    Country country = mRepository.findOne(keyWord)
    if (country !=null)
        country.setCode(code)
        return country;
    
    return null;

从 mRepository 返回的实体是存储在数据库中的。我认为您应该有另一种方法来进行这种特殊处理。

【讨论】:

【参考方案4】:

通过调用repository.findOne("Usa")(默认实现是SimpleJpaRepository.findOne)Hibernate 将使用EntityManager.find 实例化实体(如果它在数据库中找到并且不存在于一级和二级缓存中)并将传递的参数值设置为主键,使用 SessionImpl.instantiate 方法,而不是使用 Select 查询结果。 我在 Hibernate Jira 中为此问题填写了 ticket。

解决方案 1:

不要使用自然 id 作为主键:

如前所述,不建议使用业务/自然键作为主键,因为将来可能会随着业务规则的变化而变化(业务规则可以在未经许可的情况下更改!!)+如果您使用的是clustered index ,主键的字符串可能比较慢,也许你会在数据库中找到两行:Country('USA')Country('USA '),改用Surrogate Key,你可以查看这两个SO问题以获取更多详细信息:

What's the best practice for primary keys in tables? Strings as Primary Keys in SQL Database

如果您选择此选项,请不要忘记使用 @UniqueConstraint 为业务键映射唯一约束:

@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "code"))
public class Country 

    // Your code ...     

    @Column(name = "code", nullable=false)
    private String mCode;

    //...     

解决方案 2:

更改 Country.mCode 大小写:

如果您有可能将所有 Country.code 存储为大写:

@Entity
public class Country 


    private String mCode;

    protected Country()
    

    public Country(String code)
       this.mCode = code.toUpperCase()
    

    @Id
    @Column(name = "code")
    public String getCode() 
        return this.mCode;
    

   // Hibernate will always use this setter to assign mCode value
   private void setCode(String code) 
       this.mCode = code.toUpperCase();
   

解决方案 3:

自定义 CrudRepository:

在向您的存储库添加自定义函数时,您应该始终摆脱默认的findOne(String),这样您就可以强制其他人使用“最安全”的方法。

解决方案 3.1:

将custom implementations 用于mRepository 查找方法(我将其命名为findOneWithRightStringCase):

public interface CountryRepositoryCustom 
    Country findOneWithRightStringCase(String id);    


public class CountryRepositoryImpl implements CountryRepositoryCustom

    @PersistenceContext private EntityManager entityManager;

    @Override
    public Country findOneRespectCase(String id) 
        try 
            return entityManager.createQuery("SELECT c FROM Country c WHERE c.mCode=:code", Country.class).setParameter("code", id).getSingleResult();

         catch (NoResultException ex) 
            return null;
        

    


public interface CountryRepository extends CrudRepository<Country, String>, CountryRepositoryCustom    

    @Override
    public default Country findOne(String id) 
        throw new UnsupportedOperationException("[findOne(String)] may not match the case stored in database for [Country.code] value, use findOneRespectCase(String) instead!");
    

解决方案 3.2:

您可以为您的存储库添加Query methods:

public interface CountryRepository extends CrudRepository<Country, String> 
    Country findByMCode(String mCode);

    @Override
    public default Country findOne(String id) 
        throw new UnsupportedOperationException("[findOne(String)] may not match the case stored in database for [Country.code] value, use findByMCode(String) instead!");
    

或使用@Query(JPQL):

public interface CountryRepository extends CrudRepository<Country, String> 
    @Query("select c from Country c where c.mCode= ?1")
    Country selectCountryByCode(String mCode);

    @Override
    public default Country findOne(String id) 
        throw new UnsupportedOperationException("[findOne(String)] may not match the case stored in database for [Country.code] value, use selectCountryByCode(String) instead!");
    

希望对你有帮助!

注意:我使用的是 Spring Data 1.11.8.RELEASE 和 Hibernate 5.2.10.Final。

【讨论】:

投反对票的人,请解释投反对票的原因?

从 JPA/Hibernate 中的视图加载实体

...描述】:我有一个使用Spring和Hibernate的应用程序。在我的数据库中,我需要在某些实体中加载一些视图。所以我正在尝试执行本机查询并使用从视图中检索到的数据加载类://InmyDAOclass(@Repository)publicList<My 查看详情

返回从循环中调用的数据库查询中检索到的数据的问题

】返回从循环中调用的数据库查询中检索到的数据的问题【英文标题】:IssueinreturningdataretrievedfromDBqueriescalledintheloop【发布时间】:2014-11-0307:03:15【问题描述】:我在循环中进行了多个mongoDB查询。并希望将所有结果作为一个数... 查看详情

本机查询 - 如何仅从数据库中检索实体的特定列

】本机查询-如何仅从数据库中检索实体的特定列【英文标题】:Nativequery-howretriveonlyspecificcolumsfromDBforEntity【发布时间】:2017-05-1710:00:35【问题描述】:在我的数据库中,我创建了一个带有列的表主题id(long)name(String)ifActive(0or1)tex... 查看详情

将返回从选择查询中检索到的数据的函数 - Oracle

】将返回从选择查询中检索到的数据的函数-Oracle【英文标题】:Functionthatwouldreturnthedataretrievedfromaselectquery-Oracle【发布时间】:2011-06-1214:07:03【问题描述】:我正在尝试编写一个返回选择查询结果的函数。我使用过非常基本的函... 查看详情

使用hibernatehql从oracledb检索记录时的性能问题

我正在使用HibernateHQL从OracleDB中检索40K记录。从DB获取40K记录需要3秒的时间。我在SQL开发人员中运行了相同的查询,同一查询需要0.5秒。任何人都可以建议更好的方法在更短的时间内获得记录吗?答案将休眠中的查询显示变为ON... 查看详情

Hibernate 不包含与另一个实体列表中的实体相同类型的子实体

...实体,Menu和MenuItem,如下面的源代码所示。我正在尝试从数据库中获取包含所有子实体的菜单,但Hibernate还在Menu实体的no 查看详情

从数据库中检索到的文档的 BSON 对象大小

】从数据库中检索到的文档的BSON对象大小【英文标题】:BSONobjectsizeofdocumentretrievedfromDB【发布时间】:2016-11-2309:38:51【问题描述】:Mongoshell有thebsonsize()method来获取从DB中检索到的给定文档的BSON大小。有没有办法使用PyMongo驱动... 查看详情

如何使用单个查询联合从另一个表中检索到的表列表?

】如何使用单个查询联合从另一个表中检索到的表列表?【英文标题】:HowtoUNIONalistoftablesretrievedfromanothertablewithasinglequery?【发布时间】:2019-07-0105:13:03【问题描述】:我有一个表,其中包含PostgreSQL中的表列表:|id|table||--|------|... 查看详情

无法分配从 useState 中的 api 检索到的值

】无法分配从useState中的api检索到的值【英文标题】:CannotassignvalueretrievedfromapiinsideuseState【发布时间】:2021-08-2205:01:25【问题描述】:我想在输入字段的值发生变化时更新firmDetail的值。为此,我从API获取数据并尝试将检索到的... 查看详情

将使用 Python 从 Twitter 检索到的数据保存到文本文件中?

】将使用Python从Twitter检索到的数据保存到文本文件中?【英文标题】:SavingDataretrievedfromTwitterutilizingPythontoatextfile?【发布时间】:2012-05-0202:23:03【问题描述】:大家好,我目前正在做一些研究,并正在使用twitterapi来收集信息。... 查看详情

如何连接从数据库中检索到的值[关闭]

】如何连接从数据库中检索到的值[关闭]【英文标题】:Howtoconcatvalueswhichretrievedfromdatabase[closed]【发布时间】:2013-07-2211:39:20【问题描述】:我正在SQLServer2008中执行此查询SELECT(CONVERT(DATE,GETDATE()))它会显示结果2013-07-22。我需要将... 查看详情

如何从核心数据表 ios 中检索数据?

】如何从核心数据表ios中检索数据?【英文标题】:howdoretrievedatafromcoredatatableios?【发布时间】:2014-06-1510:17:44【问题描述】:您好,我在我的测试项目中使用核心数据。我能够将json中的内容添加到核心数据实体“书”。但是当... 查看详情

实体框架查询——加载数据优化

...】:我在查询中使用FirstOrDefault或First命令来检索与我的数据库中的其他实体有关系的实体的数据。我的问题是:集合上的FirstOrDefault或First命令是否会将与该实体关联的其他实体的所有数据加载到内存中?【问题讨论】:这取 查看详情

从实体具有 NSSet 的 coredata 中检索数据

】从实体具有NSSet的coredata中检索数据【英文标题】:retrievingdatafromcoredatawhereentityhasNSSet【发布时间】:2013-06-1000:47:14【问题描述】:我正在尝试使用谓词来获取我要搜索的数据位于NSSet中的数据@property(nonatomic,retain)NSSet*categories;@... 查看详情

如何让 NHibernate ISession 缓存主键未检索到的实体

...用户名不是主键,这意味着ISession不会缓存它,重复命中数据库,获取相同的数据。有什么方法可以配置 查看详情

如何在单个查询中从多个数据库中检索数据?(代码片段)

如果我有多个具有相同表和列的数据库,我如何使用Java中的单个查询从这些数据库中检索数据。为单个数据库做了这个,我是java的新手,请建议。publicclassMultipleDBTestpublicvoiddbConnect(Stringdb_connect_string,Stringdb_userid,Stringdb_password)try... 查看详情

显示从 http 调用 Angular 检索到的更新数据

】显示从http调用Angular检索到的更新数据【英文标题】:ShowupdateddatathathasbeenretrievedfromhttpcallAngular【发布时间】:2020-08-0712:35:35【问题描述】:我正在将包含嵌套数组的数据响应中的数据推送到我的组件中的静态数据数组中。以... 查看详情

java示例代码_将从数据库检索到的字符串值与程序中定义的字符串值进行比较

java示例代码_将从数据库检索到的字符串值与程序中定义的字符串值进行比较 查看详情