首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
华为云
V2EX  ›  Java

关于 Spring data jpa 写原生 sql 的问题

  •  
  •   tamer · 138 天前 · 3704 次点击
    这是一个创建于 138 天前的主题,其中的信息可能已经有所发展或是发生改变。

    官网示例中的 @Query(nativeQuery=true) 原生 sql 语句都是

    select * ....

    , 结果自己用发现, select 部分字段的话, 缺失的字段会被抛出异常:... Column xxx not found.

    比如 User 包含 id 和 name, 如果我只 select id from ...., 就会报错. 然后 gg

    好不容易从 mybatis 切换到 data jpa, 结果最想要的功能没法用,

    stack, 百度 都找不到解决方案, 是 jpa 太小众了, 还是我搜索姿势不对....

    要是放弃了, 实在不甘心啊...

    有没有也遇到过此问题的道友, 抱拳了 老铁

    
    
    
    
    50 回复  |  直到 2018-07-09 09:57:12 +08:00
        1
    tamer   138 天前
    @glues 老铁
        2
    AltairT   138 天前 via iPhone
    关注一下 感觉以后是主流技术了
        3
    airfling   138 天前 via Android
    部分字段只,如果只是一个 id 你可以用 list<idtype>,多的话可以用 list<map>
        4
    shalk   138 天前 via iPhone
    select user.id from user
        5
    Sharuru   138 天前 via Android
    通常是映射的结果集不正确。

    另外,JPA 应用一般都是选择实体,通过 Stream 取得自己需要的列。
        6
    tamer   138 天前
    @shalk 不行, 版本: Maven: org.springframework.data:spring-data-jpa:2.0.8.RELEASE
        7
    tamer   138 天前
    @Sharuru 官网给出的例子只有 select *, 如果我把所有列都放到 select 语句中就可以成功执行, 但如果缺少某某列, 就会报错,

    select user_id from : xx
    select user_id , username from : √√
    所以可以判断映射的字段是匹配的
        8
    lhx2008   137 天前 via Android
    可以返回一个 int 值再自己装?不知道有什么安全性考虑
        9
    tamer   137 天前
    @airfling map 确实可以映射, 但是除了直接从库中返回给前端的请求外, 涉及到对 User 的操作, map 就很蓝瘦了呀
        10
    tamer   137 天前
    @lhx2008 如果所有原生 sql 都必须这样手动封装一次....... 那 jpa 提供的一些蝇头小利真的被 mybatis 完爆啦
        11
    lhx2008   137 天前
    @tamer 但是全部拉出来并没有多多少资源,有洁癖还是用 mybatis 吧
        12
    lhx2008   137 天前
    我猜可能是如果有关联对象,会造成很严重的问题
        13
    tamer   137 天前
    @lhx2008 对于 json 字段, 取的话, 如果每次都必须强制获取全部, 还是很可怕的, 如果解决不了, 我还是只有换回 mybatis 啦
        14
    lhx2008   137 天前
    或者你重新封装一个类,只有 id,然后再让真正的 user 继承这个类,但是调用的时候也只能调用那个父类了
        15
    chocotan   137 天前   ♥ 1
    select 部分字段 返回值改成 List<Object[]>

    既然都是原生 sql 的话那用 jpa 的意义何在......
        17
    lhx2008   137 天前
    @tamer
    json 返回不是大问题,你本来就应该在 json 返回的时候重新封装一个对象,直出数据库真的好吗
        18
    tamer   137 天前
    @lhx2008 一般情况下, 对于有的属性 实体类则被赋值, 没有的属性自动置为 null, 这不是 orm 框架应该做的吗

    Hibernate 都可以自动映射, jpa 封装了 Hibernate 居然就不支持了...
        19
    tamer   137 天前
    @chocotan orm 自动映射的规则不是只要字段名一致, 有值就赋值, 没有值则为 NULL 吗? 现在 jpa 强制要求实体类每一个属性都必须有值, 这才是奇怪的地方把, Hibernate 和 Mybatis 都不是这样吧
        20
    sagaxu   137 天前 via Android   ♥ 1
    jpa 的工作方式不同,你只能返回 List<Object[]>或者自己定义一个新类型。连返回 map 都不行。
        21
    WispZhan   137 天前 via Android   ♥ 1
    你用错了。不是你这样用。repository 是针对 entity 的操作。看看 ddd 的设计思想在用 jpa。你会发现,你之前压根就没理解 jpa 的使用场景。

    你的 repository 是针对你那个 select * 的 entity 封装的。你要查询部分字段那就重做一个 entity 绑定到你需要的表上。可以理解为做了一个 view。
        22
    ke1e   137 天前 via Android
    搭车问下 jpa 建索引如何建倒叙索引?想给时间字段建立倒叙索引
        23
    wdlth   137 天前
    你需要用投影
        24
    mmdsun   137 天前 via Android
    告诉个黑科技。如果是 hql 用 new 包名.类名(构造函数)直接获取投影对象。原生 SQL 要写注解配置 mapper。 @SqlResultSetMappings 用列或构造函数映射类
        25
    ZSeptember   137 天前 via Android
    querydsl
        26
    kevinhwang   137 天前 via Android
    jpa 也就是做做简单的应用,过度强调 orm 性能和可维护性差。抛开性能讲可维护性,你能理解其他的程序员往你的 entity 加各种字段吗?
        28
    caotian   137 天前
    弄个 Interface 比如叫 UserLite, 只有一个方法 getId(), 然后查询那里返回值用 List<UserLite>, 再 select id 就不会出错了
        29
    letitbesqzr   137 天前
    querydsl 试试
        30
    srx1982   137 天前
    你看看实体上的字段是不是标了 @NotNull
        31
    q397064399   137 天前   ♥ 1
    JPA 是针对聚合实体的,,你那种 不要一部分字段. 我很难理解. , 你要精确控制这数据库这一小段 IO 的话
    就应该另外再建个实体 映射到同一张表上。而且从仓储层拿出来的对象 是不完整的话 也违反了最小惊讶原则,
    后面谁调你接口 一不小心就弄了 NPE 很尴尬,数据库都是不提倡使用 NULL 值的,

    如果只是取一部分数据,这个对象不用修改,那应该建模为值对象而不是实体。
    另外用 JPA 最好还是了解下 实体 聚合 值对象,JPA 就是为这玩意设计的。
        32
    q397064399   137 天前
    另外用 JPA 就应该从 SQL 中跳出来,关注实体建模, 很多时候都是反模式
        33
    yzmm   137 天前
    自定义 Repository 就可以了
        34
    zxyroy   137 天前
    如果你只 select id,返回值应该是 id,否则要给返回类设构造器。另外 JQL 是支持 new class 的
        35
    dbpe   137 天前
    用 jpa 就不要用原生 sql 了。。另外试下 querydsl

    PS:学到了楼上的一些方法了
        36
    ren2881971   137 天前
    自己定义 factory-class 然后写一个 baseRepository 在里面用 EntityManager 就可以直接写原生 sql 了,然后撸起袖子就是干。
    搜索关键词 jpa:repositories、factory-class、JpaRepositoryFactoryBean、JpaRepositoryFactory。
    ps:谁开发后台只用 select * 啊。 开玩笑。。
        37
    ren2881971   137 天前
    <jpa:repositories base-package="com.jit.ota4.*.*.repository" repository-impl-postfix="Impl" factory-class="com.jit.ota4.basic.repository.BaseRepositoryFactoryBean" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>


    public class BaseRepositoryFactoryBean<R extends JpaRepository<S, ID>, S, ID extends Serializable>
    extends JpaRepositoryFactoryBean<R, S, ID>{
    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager e) {
    return new BaseRepositoryFactory(e);
    }
    }


    public class BaseRepositoryFactory<S,ID extends Serializable> extends JpaRepositoryFactory {
    public BaseRepositoryFactory(EntityManager entityManager) {
    super(entityManager);
    }

    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
    return BaseRepository.class;
    }

    @Override
    protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
    return new BaseRepositoryImpl(information.getDomainType(), entityManager);
    }
    }


    public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {


    /**
    * @param sql 原生 sql 语句
    * @param param 动态执行参数 type:List
    * @return 返回执行的结果集条数
    */
    public int executeUpdateBySQL(String sql,List<Object> param);
    /**
    * @param sql 原生 sql 语句
    * @param param 动态执行参数 type:Map
    * @return 返回执行的结果集条数
    */
    public int executeUpdateBySQL(String sql,Map<String,Object> param);
    /**
    * @param hql hql 语句
    * @param param 动态执行参数 type:List
    * @return 返回执行的结果集条数
    */
    public int executeUpdateByHql(String hql,List<Object> param);
    /**
    * @param hql hql 语句
    * @param param 动态执行参数 type:Map
    * @return 返回执行的结果集条数
    */
    public int executeUpdateByHql(String hql,Map<String,Object> param);

    /**
    * @param hql hql 语句
    * @param param 动态查询参数 type:List
    * @param t 单实例类型
    * @return 单实例结果集
    */
    public List<T> findByHql(String hql,List<Object> param,Class<T> t);

    /**
    * @param hql hql 语句
    * @param param 动态查询参数 type:List
    * @param t 单实例类型
    * @param pageNo 页码数
    * @param pageSize 每页条数
    * @return 单实例结果集
    */
    public List<T> findByHqlWithPage(String hql,List<Object> param,Class<T> t,int pageNo,int pageSize);

    /**
    * @param hql hql 语句
    * @param param 动态查询参数 type:List
    * @return 自定义字段返回结果集
    */
    public List<Object[]> findByHql(String hql,List<Object> param);

    /**
    * @param hql hql 语句
    * @param param 动态查询参数 type:List
    * @param pageNo 页码数
    * @param pageSize 每页条数
    * @return 自定义字段返回结果集
    */
    public List<Object[]> findByHqlWithPage(String hql,List<Object> param,int pageNo,int pageSize);

    /**
    * @param sql 原生 sql 语句
    * @param param 动态查询参数 type:List
    * @return 执行原生 sql 返回结果集
    */
    public List<Object[]> findBySQL(String sql,List<Object> param);

    /**
    * @param sql 原生 sql 语句
    * @param param 动态查询参数 type:List
    * @param pageNo 页码数
    * @param pageSize 每页条数
    * @return
    */
    public List<Object[]> findBySQLWithPage(String sql,List<Object> param ,int pageNo,int pageSize);


    }
        38
    ren2881971   137 天前
    糟糕 没排版。。。
        39
    dbpe   137 天前
    @ren2881971 贴个 github 的地址把。。
        40
    tamer   137 天前
    @ren2881971 感谢 , 主要是被 jpa 的方法名称推断惊艳到了, 连自动生成语句都帮程序员解决了所以就想用用, 实际除了最简单的 select 语句外, 我个人都基本倾向写原生 sql, 所有有这个问题
        41
    ren2881971   137 天前
    @dbpe https://github.com/ren2881971/spring4mvc-jpa-demo 好几年前搭建的。。。。
        42
    ren2881971   137 天前
    @tamer 估计是你用 mybatis 用习惯了~ 如果一直用 hibernate 的话 没什么感觉~
        43
    nicevar   137 天前
    配置一个 JpaTransactionManager 获取 EntityManager 直接操作原生 sql
        44
    beginor   137 天前 via Android
    JPA 可以认为就是 Hibernate, 我在 .Net 上用的是 NHibernate, 也有类似这样的需求, 复杂的查询用 SQL 解决, 在 .Net 下有现成的轮子 Dapper https://github.com/StackExchange/Dapper,Java 上应该也有类似 Dapper 的轮子吧?
        45
    beginor   137 天前 via Android
    稍微不注意排版就错误,Dapper 的地址是 https://github.com/StackExchange/Dapper。

    V 站啥时候回复也支持 md 呢?
        46
    a812159920   137 天前
    EntityManager API 提供了创建 Query 实例以执行原生 SQL 语句的 createNativeQuery 方法。
    链接: https://blog.csdn.net/opera95/article/details/78207416
    不谢
        47
    mmdsun   136 天前 via Android
    @a812159920 这个原生 SQL 查询非 select * 返回的是 list<object[]>感觉好麻烦。不能直接返回 list<user>,然后没有查的字段返回 null 么。
        48
    dbpe   136 天前
    @beginor 因为 hibernate 实现 jpa 的标准。。。
        50
    reAsOn   135 天前
    @ke1e 看你用什么版本的数据库了,和 jpa 关系不大,如果是新的 比如 mysql 8 可以直接建倒序的索引,如果比较老,可以用虚拟列,把时间 neg 一遍然后建普通的正序索引
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2406 人在线   最高记录 3911   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.1 · 23ms · UTC 13:04 · PVG 21:04 · LAX 05:04 · JFK 08:04
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1