স্ট্রিংয়ের জন্য Jdbctemplate ক্যোয়ারী: খালি ফলাফলগুলি ডেটা অ্যাক্সেসএক্সেপশন: ভুল ফলাফলের আকার: প্রত্যাশিত 1, প্রকৃত 0


105

আমি Db থেকে একক স্ট্রিংয়ের মান পুনরুদ্ধার করতে Jdbctemplate ব্যবহার করছি। এই আমার পদ্ধতি।

    public String test() {
        String cert=null;
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
             where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        cert = (String) jdbc.queryForObject(sql, String.class); 
        return cert;
    }

আমার দৃশ্যে আমার ক্যোয়ারীতে হিট না পাওয়া সম্পূর্ণ সম্ভব তাই আমার প্রশ্নটি হল আমি কীভাবে নীচের ত্রুটি বার্তাটি পেতে পারি।

EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

আমার কাছে মনে হবে যে আমার ব্যতিক্রম ছোঁড়ার পরিবর্তে কেবল একটি নাল ফিরে পাওয়া উচিত। আমি এটা কিভাবে ঠিক করবো? আগাম ধন্যবাদ.

উত্তর:


179

JdbcTemplate সালে queryForInt, queryForLong, queryForObjectসব ধরনের পদ্ধতি আশা যে প্রশ্নের সাথে মৃত্যুদন্ড কার্যকর এক এবং একমাত্র এক সারি ফিরে আসবে। যদি আপনি কোনও সারি বা একাধিক সারি না পান তবে ফলস্বরূপ IncorrectResultSizeDataAccessException। এখন সঠিক উপায় এই ব্যতিক্রমটি ধরা না বা EmptyResultDataAccessException, তবে আপনি যে ক্যোয়ারীটি ব্যবহার করছেন সেটি কেবল একটি সারিতে ফিরে আসবে তা নিশ্চিত করুন। যদি একেবারেই সম্ভব না হয় তবে queryপরিবর্তে পদ্ধতিটি ব্যবহার করুন।

List<String> strLst  = getJdbcTemplate().query(sql,new RowMapper {

  public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
        return rs.getString(1);
  }

});

if ( strLst.isEmpty() ){
  return null;
}else if ( strLst.size() == 1 ) { // list contains exactly 1 element
  return strLst.get(0);
}else{  // list contains more than 1 elements
  //your wish, you can either throw the exception or return 1st element.    
}

নীচে উল্লিখিত হিসাবে, এখানে একমাত্র ত্রুটিটি হ'ল যদি ফিরতি টাইপটি জটিল ধরণের ছিল তবে আপনি একাধিক অবজেক্ট তৈরি করে একটি তালিকা ইনস্ট্যান্ট করছেন, অযথাও ResultSet.next()বলা হবে। ResultSetExtractorএই ক্ষেত্রে একটি ব্যবহার করা অনেক বেশি কার্যকর সরঞ্জাম।
ব্রেট রায়ান

3
বেনাম শ্রেণীর সংজ্ঞায় একটি অনুপস্থিত বন্ধনী রয়েছে - নতুন রো-ম্যাপার ()
জ্যানিস কলুজস

আমি এই নিয়ে ব্রেটের সাথে আছি। ResultSetExtractor ক্লিনার :) হল
laher

2
হাই @Rakesh, কেন না শুধুমাত্র return nullমধ্যে catch(EmptyResultDataAccessException exception){ return null; }?
বিশাল জানজারুকিয়া

1
হে! আমি কি জিজ্ঞাসা করতে পারি যে 'এখন সঠিক উপায়টি এই ব্যতিক্রমটি ধরা না কেন', যদি আপনি কোয়ারফোর্ড অবজেক্টটি ব্যবহার করছেন তবে বিবেচনা করছেন? ক্যোয়ারফোর্ড অবজেক্টের ক্ষেত্রে ব্যতিক্রম ধরা কী হবে? ধন্যবাদ :)
মাইকেল স্টোকস

48

আপনি এ এর ResultSetExtractorপরিবর্তে একটি ব্যবহার করতে পারেন RowMapper। উভয়ই একে অপরের মতো সহজ, পার্থক্য কেবল আপনিই কল করেন ResultSet.next()

public String test() {
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN "
                 + " where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    return jdbc.query(sql, new ResultSetExtractor<String>() {
        @Override
        public String extractData(ResultSet rs) throws SQLException,
                                                       DataAccessException {
            return rs.next() ? rs.getString("ID_NMB_SRZ") : null;
        }
    });
}

ResultSetExtractorযোগ বেনিফিট যে সমস্ত ক্ষেত্রে যেখানে একাধিক সারি আছে অথবা কোন সারি ফিরে সব ব্যবস্থা করতে সক্ষম হয়েছে।

আপডেট : বেশ কয়েক বছর ধরে এবং আমার কিছু ভাগ করার কৌশল রয়েছে। JdbcTemplateজাভা 8 ল্যাম্বডাসের সাথে দুর্দান্তভাবে কাজ করে যা নিম্নলিখিত উদাহরণগুলির জন্য ডিজাইন করা হয়েছে তবে আপনি এটি অর্জনের জন্য খুব সহজেই একটি স্ট্যাটিক বর্গ ব্যবহার করতে পারেন।

প্রশ্নটি সাধারণ ধরণের সম্পর্কে হলেও এই উদাহরণগুলি ডোমেন অবজেক্টগুলি নিষ্কাশনের সাধারণ ক্ষেত্রে গাইড হিসাবে কাজ করে।

প্রথম বন্ধ. ধরা যাক আপনার সরলতার জন্য দুটি বৈশিষ্ট্য সহ একটি অ্যাকাউন্ট অবজেক্ট রয়েছে Account(Long id, String name)। আপনি সম্ভবত RowMapperএই ডোমেন অবজেক্টের জন্য একটি থাকতে চান ।

private static final RowMapper<Account> MAPPER_ACCOUNT =
        (rs, i) -> new Account(rs.getLong("ID"),
                               rs.getString("NAME"));

আপনি এখন একজন পদ্ধতি মধ্যে সরাসরি এই ম্যাপার ব্যবহার করতে পারেন ম্যাপ Accountএকটি ক্যোয়ারী থেকে ডোমেন বস্তু ( jtএকটি হল JdbcTemplateউদাহরণস্বরূপ)।

public List<Account> getAccounts() {
    return jt.query(SELECT_ACCOUNT, MAPPER_ACCOUNT);
}

দুর্দান্ত, তবে এখন আমরা আমাদের মূল সমস্যাটি চাই এবং আমরা RowMapperআমাদের ম্যাপিংটি পুনরায় ব্যবহার করে আমাদের জন্য ম্যাপিংটি ব্যবহার করি।

public Account getAccount(long id) {
    return jt.query(
            SELECT_ACCOUNT,
            rs -> rs.next() ? MAPPER_ACCOUNT.mapRow(rs, 1) : null,
            id);
}

দুর্দান্ত, তবে এটি এমন একটি প্যাটার্ন যা আপনি আবার করতে চান এবং চান। সুতরাং আপনি ResultSetExtractorকাজের জন্য একটি নতুন তৈরির জন্য জেনেরিক কারখানা পদ্ধতি তৈরি করতে পারেন ।

public static <T> ResultSetExtractor singletonExtractor(
        RowMapper<? extends T> mapper) {
    return rs -> rs.next() ? mapper.mapRow(rs, 1) : null;
}

ResultSetExtractorএখনই তৈরি করা তুচ্ছ হয়ে ওঠে।

private static final ResultSetExtractor<Account> EXTRACTOR_ACCOUNT =
        singletonExtractor(MAPPER_ACCOUNT);

public Account getAccount(long id) {
    return jt.query(SELECT_ACCOUNT, EXTRACTOR_ACCOUNT, id);
}

আমি আশা করি এটি দেখানোতে সহায়তা করে যে আপনি এখন আপনার ডোমেনকে আরও সহজ করার জন্য শক্তিশালী উপায়ে অংশগুলি খুব সহজেই একত্রিত করতে পারেন।

আপডেট 2 : নাল পরিবর্তে alচ্ছিক মান জন্য একটি ption চ্ছিক সঙ্গে একত্রিত ।

public static <T> ResultSetExtractor<Optional<T>> singletonOptionalExtractor(
        RowMapper<? extends T> mapper) {
    return rs -> rs.next() ? Optional.of(mapper.mapRow(rs, 1)) : Optional.empty();
}

যা এখন ব্যবহৃত হলে নিম্নলিখিতগুলি থাকতে পারে:

private static final ResultSetExtractor<Optional<Double>> EXTRACTOR_DISCOUNT =
        singletonOptionalExtractor(MAPPER_DISCOUNT);

public double getDiscount(long accountId) {
    return jt.query(SELECT_DISCOUNT, EXTRACTOR_DISCOUNT, accountId)
            .orElse(0.0);
}

21

এটি কোনও ভাল সমাধান নয় কারণ আপনি নিয়ন্ত্রণ প্রবাহের ব্যতিক্রমগুলির উপর নির্ভর করছেন। আপনার সমাধানে ব্যতিক্রমগুলি পাওয়া স্বাভাবিক, তাদের লগ এ থাকা স্বাভাবিক।

public String test() {
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    List<String> certs = jdbc.queryForList(sql, String.class); 
    if (certs.isEmpty()) {
        return null;
    } else {
        return certs.get(0);
    }
}

আমার সমাধানটি সবচেয়ে মার্জিত না হলেও কমপক্ষে আমার কাজ করে। আপনি ক্যোয়ারফোরঅবজেক্টলিস্টের একটি উদাহরণ দেন যা জেডিবিসিটেমলেট সহ কোনও বিকল্পও নয়।
বায়রন

1
কেবল এখানে অপূর্ণতা হ'ল যদি ফিরতি টাইপটি কোনও জটিল ধরণের ছিল তবে আপনি একাধিক অবজেক্ট তৈরি করতেন এবং একটি তালিকা ইনস্ট্যান্টিয়েট ResultSet.next()করতেন , অকারণেও তাকে ডাকা হত। ResultSetExtractorএই ক্ষেত্রে একটি ব্যবহার করা অনেক বেশি কার্যকর সরঞ্জাম।
ব্রেট রায়ান

এবং যদি কোনও মূল্য না থাকা একটি বিকল্প, তবে একের বেশি না থাকে? আমার এই প্যাটার্নটি প্রায়শই থাকে এবং আমি এই উদ্দেশ্যটির জন্য স্প্রিংয়ে একটি বিকল্প জিজ্ঞাসা রাখতে চাই For
গিলাইম

7

প্রকৃতপক্ষে, আপনি JdbcTemplateনিজের পছন্দ অনুসারে খেলতে এবং কাস্টমাইজ করতে পারেন । আমার পরামর্শটি হ'ল এরকম কিছু করা:

public String test() {
    String cert = null;
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN
        where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    ArrayList<String> certList = (ArrayList<String>) jdbc.query(
        sql, new RowMapperResultSetExtractor(new UserMapper()));
    cert =  DataAccessUtils.singleResult(certList);

    return cert;
}

এটি আসল হিসাবে কাজ করে jdbc.queryForObjectতবে throw new EmptyResultDataAccessExceptionকখন না size == 0


@Abdull UserMapper implements RowMapper<String>
ব্রেট রায়ান 20

আমি মনে করি এটি এখানে সবচেয়ে ভাল উত্তর হিসাবে এটি সংক্ষিপ্ত বাক্য গঠন দেয়
স্টান সোকলভ

DataAccessUtils.singleResult(...)আমি যা খুঁজছিলাম ধন্যবা
ছকড়া

7

যেহেতু কোনও ডেটা না থাকাকালীন নাল ফিরে পাওয়া এমন কিছু যা আমি প্রায়শই জিজ্ঞাসা করি ফোরওবজেক্ট ব্যবহার করার সময় আমি জেডিবিসিটেমপ্লেটটি প্রসারিত করতে এবং নীচের মতো অনুরূপ একটি ক্যোয়ারেরফরনব্যাবলঅবজেক্ট পদ্ধতি যুক্ত করতে দরকারী বলে মনে করেছি।

public class JdbcTemplateExtended extends JdbcTemplate {

    public JdbcTemplateExtended(DataSource datasource){
        super(datasource);
    }

    public <T> T queryForNullableObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
        List<T> results = query(sql, rowMapper);

        if (results == null || results.isEmpty()) {
            return null;
        }
        else if (results.size() > 1) {
            throw new IncorrectResultSizeDataAccessException(1, results.size());
        }
        else{
            return results.iterator().next();
        }
    }

    public <T> T queryForNullableObject(String sql, Class<T> requiredType) throws DataAccessException {
        return queryForObject(sql, getSingleColumnRowMapper(requiredType));
    }

}

আপনি এখন আপনার কোডটিতে একইভাবে আপনি ক্যোয়ারফোর্ডঅবজেক্ট ব্যবহার করেছেন

String result = queryForNullableObject(queryString, String.class);

আমি জানতে আগ্রহী অন্য কেউ যদি এটি একটি ভাল ধারণা মনে করে?


1
এটি হ'ল এবং এটি বসন্তে হওয়া উচিত
গিলিয়াম

6

ঠিক আছে, আমি এটি বুঝতে পেরেছি। আমি এটিকে কেবল একবার চেষ্টা করে জড়িয়ে রেখেছি এবং নালটিকে আবার পাঠিয়েছি।

    public String test() {
            String cert=null;
            String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
                     where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
            try {
                Object o = (String) jdbc.queryForObject(sql, String.class);
                cert = (String) o;
            } catch (EmptyResultDataAccessException e) {
                e.printStackTrace();
            }
            return cert;
    }

1
আমি দেখতে পাচ্ছি না কেন এটি এত খারাপ এবং কেন আপনি এর জন্য এতটা নিম্নস্তর পেয়েছিলেন, 'ব্যতিক্রমধর্মী কোনও প্রোগ্রাম প্রবাহ নয়' নীতিতে মৌলবাদী হওয়ার জন্য প্রস্তুতি গ্রহণ করুন। আমি প্রিন্সট্যাক ট্রেসটিকে কেবলমাত্র একটি ব্যাখ্যা দিয়ে মন্তব্যটির সাথে প্রতিস্থাপন করব এবং অন্য কিছু করব না।
গিলাইম

4

জাভা 8 বা তার বেশি ব্যবহার করে আপনি একটি Optionalএবং জাভা স্ট্রিম ব্যবহার করতে পারেন ।

সুতরাং আপনি কেবল JdbcTemplate.queryForList()পদ্ধতিটি ব্যবহার করতে পারেন , একটি স্ট্রিম তৈরি Stream.findFirst()করতে পারেন এবং ব্যবহার করতে পারেন যা স্ট্রিমের প্রথম মান বা একটি খালি ফিরিয়ে দেবে Optional:

public Optional<String> test() {
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    return jdbc.queryForList(sql, String.class)
            .stream().findFirst();
}

ক্যোয়ারির কার্যকারিতা উন্নত করতে LIMIT 1আপনি আপনার ক্যোয়ারিতে সংযোজন করতে পারেন , তাই ডাটাবেস থেকে 1 টির বেশি আইটেম স্থানান্তরিত হবে না।


1
সুন্দর এবং পরিষ্কার। কোনও অতিরিক্ত আইএফ বা ল্যাম্বডাস নেই। আমি এটা পছন্দ করি.
বেশইটার

2

আপনি একটি গোষ্ঠী ফাংশন ব্যবহার করতে পারেন যাতে আপনার ক্যোয়ারী সর্বদা ফল দেয়। অর্থাত

MIN(ID_NMB_SRZ)

1

পোস্টগ্রিসে, আপনি প্রায় কোনও একক মান কোয়েরিটিকে কোনও মান বা নালাকে মোড়কে ফিরিয়ে দিতে পারেন:

SELECT (SELECT <query>) AS value

এবং তাই কলারে জটিলতা এড়ান।


1

যেহেতু getJdbcTemplate (।

Map<String, String> loginMap =null;
try{
    loginMap = getJdbcTemplate().queryForMap(sql, new Object[] {CustomerLogInInfo.getCustLogInEmail()});
}
catch(EmptyResultDataAccessException ex){
    System.out.println("Exception.......");
    loginMap =null;
}
if(loginMap==null || loginMap.isEmpty()){
    return null;
}
else{
    return loginMap;
}

0

আমি এর আগে এর সাথে ডিল করেছি এবং বসন্ত ফোরামে পোস্ট করেছি had

http://forum.spring.io/forum/spring-projects/data/123129-frustrated-with-emptyresultdataaccessexception

আমরা প্রাপ্ত পরামর্শটি হ'ল এক ধরণের এসকিউকিউয়ারি ব্যবহার করা। ডিবি থেকে কোনও মান বের করার চেষ্টা করার সময় আমরা কী করেছি তার উদাহরণ এখানে দেওয়া হয়েছে might

@Component
public class FindID extends MappingSqlQuery<Long> {

        @Autowired
        public void setDataSource(DataSource dataSource) {

                String sql = "Select id from address where id = ?";

                super.setDataSource(dataSource);

                super.declareParameter(new SqlParameter(Types.VARCHAR));

                super.setSql(sql);

                compile();
        }

        @Override
        protected Long mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getLong(1);
        }

ডিএও-তে তখন আমরা কেবল কল করি ...

Long id = findID.findObject(id);

কর্মক্ষমতা সম্পর্কে পরিষ্কার নয়, তবে এটি কাজ করে এবং ঝরঝরে।


0

বায়রনের জন্য, আপনি এটি চেষ্টা করতে পারেন ..

public String test(){
                String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
                     where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
                List<String> li = jdbcTemplate.queryForList(sql,String.class);
                return li.get(0).toString();
        }

0

করা

    jdbcTemplate.queryForList(sql, String.class)

কাজ করুন, নিশ্চিত করুন যে আপনার jdbcTemplate টাইপ হয়েছে

    org.springframework.jdbc.core.JdbcTemplate

0

আমরা ক্যোয়ারফোরঅবজেক্টের পরিবর্তে কোয়েরি ব্যবহার করতে পারি, ক্যোয়ারী এবং ক্যোয়ারী ফোরঅবজেক্টের মধ্যে প্রধান পার্থক্যটি হ'ল অবজেক্টের ক্যোরি রিটার্নের তালিকা (সারি ম্যাপার রিটার্ন টাইপের উপর ভিত্তি করে) এবং সেই তালিকাটি ফাঁকা হতে পারে যদি ডাটাবেস থেকে কোনও তথ্য না পাওয়া যায় তবে ক্যোয়ারফোর্ড অবজেক্ট সর্বদা কেবল একক বস্তুর প্রত্যাশা করে ডিবি থেকে নাল বা একাধিক সারি পাওয়া যায় নি এবং ফলাফল যদি খালি থাকে তবে কোয়েরিফোর্ড অবব্যাক্টটি EmptyResultDataAccessException নিক্ষেপ করে, আমি কোয়েরি ব্যবহার করে একটি কোড লিখেছিলাম যা নাল ফলাফলের ক্ষেত্রে EmptyResultDataAccessException এর সমস্যাটি কাটিয়ে উঠবে।

----------


public UserInfo getUserInfo(String username, String password) {
      String sql = "SELECT firstname, lastname,address,city FROM users WHERE id=? and pass=?";
      List<UserInfo> userInfoList = jdbcTemplate.query(sql, new Object[] { username, password },
              new RowMapper<UserInfo>() {
                  public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
                      UserInfo user = new UserInfo();
                      user.setFirstName(rs.getString("firstname"));
                      user.setLastName(rs.getString("lastname"));
                      user.setAddress(rs.getString("address"));
                      user.setCity(rs.getString("city"));

                      return user;
                  }
              });

      if (userInfoList.isEmpty()) {
          return null;
      } else {
          return userInfoList.get(0);
      }
  }

0

আইএমএইচও ফেরত একটি null খারাপ সমাধান কারণ এখন আপনার সামনের প্রান্তের ক্লায়েন্টে এটি প্রেরণ এবং ব্যাখ্যা করার সমস্যা রয়েছে। আমার একই ত্রুটি ছিল এবং আমি এটিকে কেবল একটি দিয়ে ফিরে এসে সমাধান করেছি List<FooObject>। আমি ব্যবহার করতামJDBCTemplate.query()

সামনের প্রান্তে (কৌণিক ওয়েব ক্লায়েন্ট), আমি কেবল তালিকাটি পরীক্ষা করি এবং যদি এটি খালি হয় (শূন্য দৈর্ঘ্যের), কোনও রেকর্ড পাওয়া যায় নি বলে এটি ব্যবহার করুন।


-1

আমি কেবল এই "EmptyResultDataAccessException" ধরি

public Myclass findOne(String id){
    try {
        Myclass m = this.jdbcTemplate.queryForObject(
                "SELECT * FROM tb_t WHERE id = ?",
                new Object[]{id},
                new RowMapper<Myclass>() {
                    public Myclass mapRow(ResultSet rs, int rowNum) throws SQLException {
                        Myclass m = new Myclass();
                        m.setName(rs.getString("name"));
                        return m;
                    }
                });
        return m;
    } catch (EmptyResultDataAccessException e) { // result.size() == 0;
        return null;
    }
}

তারপরে আপনি যাচাই করতে পারেন:

if(m == null){
    // insert operation.
}else{
    // update operation.
}

আমরা ক্যোয়ারীফোরঅবজেক্টের পরিবর্তে কোয়েরিটি ব্যবহার করতে পারি
আবাহা জোহরি

1
সাধারণত এই জাতীয় ব্যতিক্রমকে খারাপ ব্যবহার করার জন্য অনুশীলন হিসাবে বিবেচনা করা হয়। ব্যতিক্রমগুলি অনুমানযোগ্য প্রোগ্রাম লজিক প্রবাহের জন্য নয়, তারা ব্যতিক্রমী পরিস্থিতিতে।
ক্রিস বেকার 14
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.