[{"data":1,"prerenderedAt":3062},["ShallowReactive",2],{"tag-performance":3},[4],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"layout":11,"date":12,"subtitle":13,"image":14,"optimized_image":14,"category":15,"tags":16,"author":21,"paginate":7,"body":22,"_type":3056,"_id":3057,"_source":3058,"_file":3059,"_stem":3060,"_extension":3061},"/posts/n-plus-one-queries","posts",false,"","The N+1 Query Problem: Finding and Fixing a Slow Search Endpoint","Understand the N+1 query problem, detect it in a Java service backed by PostgreSQL, and compare projections, fetch joins, batching, and pagination strategies.","post","2025-04-15T11:00:00.000Z","Follow the SQL behind a Java endpoint before choosing a fix","/assets/img/uploads/n-plus-one-queries.jpg","code",[15,17,18,19,20],"java","postgresql","performance","database","jaimedearcos",{"type":23,"children":24,"toc":3038},"root",[25,33,38,43,54,59,66,71,83,88,375,427,558,563,844,856,869,874,1138,1143,1148,1154,1159,1164,1169,1179,1185,1192,1197,1202,1305,1318,1324,1329,1454,1485,1490,1495,1501,1514,1650,1664,1669,1730,1750,1756,1761,1767,1772,1820,1825,1959,1964,1978,1984,1989,2114,2135,2140,2217,2230,2243,2249,2254,2322,2327,2348,2353,2359,2364,2377,2382,2461,2474,2479,2915,2920,2933,2938,2944,2949,2954,2959,2964,2970,2975,2980,2986,3032],{"type":26,"tag":27,"props":28,"children":29},"element","p",{},[30],{"type":31,"value":32},"text","While talking with a teammate about a project I work on, already more than three years old, we started looking at a search endpoint in a Java service backed by PostgreSQL. Its performance was poor, with worrying spikes in average response time. An ordinary search was taking far longer than we would expect from it.",{"type":26,"tag":27,"props":34,"children":35},{},[36],{"type":31,"value":37},"That conversation stayed with me because these are the kinds of problems that can sit inside familiar code. A service has been around for years, the endpoint returns the right data, and everyone has other work to do. Familiarity can make it easy to stop asking how much work a request actually performs.",{"type":26,"tag":27,"props":39,"children":40},{},[41],{"type":31,"value":42},"The N+1 query problem is one of the fundamentals every developer working with persisted data should understand: how it happens, how to recognize it, and how to choose a fix. That includes being willing to revisit our own code. Experience should make us more curious about these problems, and more helpful when we find them together.",{"type":26,"tag":27,"props":44,"children":45},{},[46,52],{"type":26,"tag":47,"props":48,"children":49},"strong",{},[50],{"type":31,"value":51},"Your service's response times represent you and your team.",{"type":31,"value":53}," They are part of what people experience when they use something you have built. We cannot prevent every performance issue, but we can make a habit of investigating them, sharing what we learn, and leaving the service better than we found it.",{"type":26,"tag":27,"props":55,"children":56},{},[57],{"type":31,"value":58},"Let's make that practical. The order-search example below is illustrative, not a reconstruction of that project's code or measurements. It uses Spring Data JPA and Hibernate 6 to make the database interactions visible.",{"type":26,"tag":60,"props":61,"children":63},"h2",{"id":62},"what-n1-actually-means",[64],{"type":31,"value":65},"What N+1 actually means",{"type":26,"tag":27,"props":67,"children":68},{},[69],{"type":31,"value":70},"Imagine an endpoint that returns a page of orders, including each customer's name. The application first queries the orders. Then, while building the response, it retrieves a customer separately for each order.",{"type":26,"tag":27,"props":72,"children":73},{},[74,76,81],{"type":31,"value":75},"That is one initial query, followed by N additional queries for N results: ",{"type":26,"tag":47,"props":77,"children":78},{},[79],{"type":31,"value":80},"1 + N",{"type":31,"value":82},".",{"type":26,"tag":27,"props":84,"children":85},{},[86],{"type":31,"value":87},"Here is the relevant part of an entity mapping. Imports, constructors, and accessors are omitted:",{"type":26,"tag":89,"props":90,"children":93},"pre",{"className":91,"code":92,"language":17,"meta":8,"style":8},"language-java shiki shiki-themes github-dark github-light","@Entity\n@Table(name = \"purchase_order\")\npublic class PurchaseOrder {\n    @Id\n    private Long id;\n\n    private String status;\n    private Instant createdAt;\n\n    @ManyToOne(fetch = FetchType.LAZY, optional = false)\n    @JoinColumn(name = \"customer_id\", nullable = false)\n    private Customer customer;\n}\n",[94],{"type":26,"tag":15,"props":95,"children":96},{"__ignoreMap":8},[97,115,155,180,194,208,218,231,244,252,301,353,366],{"type":26,"tag":98,"props":99,"children":102},"span",{"class":100,"line":101},"line",1,[103,109],{"type":26,"tag":98,"props":104,"children":106},{"style":105},"--shiki-default:#E1E4E8;--shiki-light:#24292E",[107],{"type":31,"value":108},"@",{"type":26,"tag":98,"props":110,"children":112},{"style":111},"--shiki-default:#F97583;--shiki-light:#D73A49",[113],{"type":31,"value":114},"Entity\n",{"type":26,"tag":98,"props":116,"children":118},{"class":100,"line":117},2,[119,123,128,133,139,144,150],{"type":26,"tag":98,"props":120,"children":121},{"style":105},[122],{"type":31,"value":108},{"type":26,"tag":98,"props":124,"children":125},{"style":111},[126],{"type":31,"value":127},"Table",{"type":26,"tag":98,"props":129,"children":130},{"style":105},[131],{"type":31,"value":132},"(",{"type":26,"tag":98,"props":134,"children":136},{"style":135},"--shiki-default:#79B8FF;--shiki-light:#005CC5",[137],{"type":31,"value":138},"name",{"type":26,"tag":98,"props":140,"children":141},{"style":111},[142],{"type":31,"value":143}," =",{"type":26,"tag":98,"props":145,"children":147},{"style":146},"--shiki-default:#9ECBFF;--shiki-light:#032F62",[148],{"type":31,"value":149}," \"purchase_order\"",{"type":26,"tag":98,"props":151,"children":152},{"style":105},[153],{"type":31,"value":154},")\n",{"type":26,"tag":98,"props":156,"children":158},{"class":100,"line":157},3,[159,164,169,175],{"type":26,"tag":98,"props":160,"children":161},{"style":111},[162],{"type":31,"value":163},"public",{"type":26,"tag":98,"props":165,"children":166},{"style":111},[167],{"type":31,"value":168}," class",{"type":26,"tag":98,"props":170,"children":172},{"style":171},"--shiki-default:#B392F0;--shiki-light:#6F42C1",[173],{"type":31,"value":174}," PurchaseOrder",{"type":26,"tag":98,"props":176,"children":177},{"style":105},[178],{"type":31,"value":179}," {\n",{"type":26,"tag":98,"props":181,"children":183},{"class":100,"line":182},4,[184,189],{"type":26,"tag":98,"props":185,"children":186},{"style":105},[187],{"type":31,"value":188},"    @",{"type":26,"tag":98,"props":190,"children":191},{"style":111},[192],{"type":31,"value":193},"Id\n",{"type":26,"tag":98,"props":195,"children":197},{"class":100,"line":196},5,[198,203],{"type":26,"tag":98,"props":199,"children":200},{"style":111},[201],{"type":31,"value":202},"    private",{"type":26,"tag":98,"props":204,"children":205},{"style":105},[206],{"type":31,"value":207}," Long id;\n",{"type":26,"tag":98,"props":209,"children":211},{"class":100,"line":210},6,[212],{"type":26,"tag":98,"props":213,"children":215},{"emptyLinePlaceholder":214},true,[216],{"type":31,"value":217},"\n",{"type":26,"tag":98,"props":219,"children":221},{"class":100,"line":220},7,[222,226],{"type":26,"tag":98,"props":223,"children":224},{"style":111},[225],{"type":31,"value":202},{"type":26,"tag":98,"props":227,"children":228},{"style":105},[229],{"type":31,"value":230}," String status;\n",{"type":26,"tag":98,"props":232,"children":234},{"class":100,"line":233},8,[235,239],{"type":26,"tag":98,"props":236,"children":237},{"style":111},[238],{"type":31,"value":202},{"type":26,"tag":98,"props":240,"children":241},{"style":105},[242],{"type":31,"value":243}," Instant createdAt;\n",{"type":26,"tag":98,"props":245,"children":247},{"class":100,"line":246},9,[248],{"type":26,"tag":98,"props":249,"children":250},{"emptyLinePlaceholder":214},[251],{"type":31,"value":217},{"type":26,"tag":98,"props":253,"children":255},{"class":100,"line":254},10,[256,260,265,269,274,278,283,288,292,297],{"type":26,"tag":98,"props":257,"children":258},{"style":105},[259],{"type":31,"value":188},{"type":26,"tag":98,"props":261,"children":262},{"style":111},[263],{"type":31,"value":264},"ManyToOne",{"type":26,"tag":98,"props":266,"children":267},{"style":105},[268],{"type":31,"value":132},{"type":26,"tag":98,"props":270,"children":271},{"style":135},[272],{"type":31,"value":273},"fetch",{"type":26,"tag":98,"props":275,"children":276},{"style":111},[277],{"type":31,"value":143},{"type":26,"tag":98,"props":279,"children":280},{"style":105},[281],{"type":31,"value":282}," FetchType.LAZY, ",{"type":26,"tag":98,"props":284,"children":285},{"style":135},[286],{"type":31,"value":287},"optional",{"type":26,"tag":98,"props":289,"children":290},{"style":111},[291],{"type":31,"value":143},{"type":26,"tag":98,"props":293,"children":294},{"style":135},[295],{"type":31,"value":296}," false",{"type":26,"tag":98,"props":298,"children":299},{"style":105},[300],{"type":31,"value":154},{"type":26,"tag":98,"props":302,"children":304},{"class":100,"line":303},11,[305,309,314,318,322,326,331,336,341,345,349],{"type":26,"tag":98,"props":306,"children":307},{"style":105},[308],{"type":31,"value":188},{"type":26,"tag":98,"props":310,"children":311},{"style":111},[312],{"type":31,"value":313},"JoinColumn",{"type":26,"tag":98,"props":315,"children":316},{"style":105},[317],{"type":31,"value":132},{"type":26,"tag":98,"props":319,"children":320},{"style":135},[321],{"type":31,"value":138},{"type":26,"tag":98,"props":323,"children":324},{"style":111},[325],{"type":31,"value":143},{"type":26,"tag":98,"props":327,"children":328},{"style":146},[329],{"type":31,"value":330}," \"customer_id\"",{"type":26,"tag":98,"props":332,"children":333},{"style":105},[334],{"type":31,"value":335},", ",{"type":26,"tag":98,"props":337,"children":338},{"style":135},[339],{"type":31,"value":340},"nullable",{"type":26,"tag":98,"props":342,"children":343},{"style":111},[344],{"type":31,"value":143},{"type":26,"tag":98,"props":346,"children":347},{"style":135},[348],{"type":31,"value":296},{"type":26,"tag":98,"props":350,"children":351},{"style":105},[352],{"type":31,"value":154},{"type":26,"tag":98,"props":354,"children":356},{"class":100,"line":355},12,[357,361],{"type":26,"tag":98,"props":358,"children":359},{"style":111},[360],{"type":31,"value":202},{"type":26,"tag":98,"props":362,"children":363},{"style":105},[364],{"type":31,"value":365}," Customer customer;\n",{"type":26,"tag":98,"props":367,"children":369},{"class":100,"line":368},13,[370],{"type":26,"tag":98,"props":371,"children":372},{"style":105},[373],{"type":31,"value":374},"}\n",{"type":26,"tag":27,"props":376,"children":377},{},[378,380,386,388,394,396,402,404,409,411,417,419,425],{"type":31,"value":379},"Assume ",{"type":26,"tag":15,"props":381,"children":383},{"className":382},[],[384],{"type":31,"value":385},"Customer",{"type":31,"value":387}," maps to the ",{"type":26,"tag":15,"props":389,"children":391},{"className":390},[],[392],{"type":31,"value":393},"customer",{"type":31,"value":395}," table and has ",{"type":26,"tag":15,"props":397,"children":399},{"className":398},[],[400],{"type":31,"value":401},"id",{"type":31,"value":403}," and ",{"type":26,"tag":15,"props":405,"children":407},{"className":406},[],[408],{"type":31,"value":138},{"type":31,"value":410}," fields. The repository retrieves a bounded list; using ",{"type":26,"tag":15,"props":412,"children":414},{"className":413},[],[415],{"type":31,"value":416},"List",{"type":31,"value":418}," with ",{"type":26,"tag":15,"props":420,"children":422},{"className":421},[],[423],{"type":31,"value":424},"Pageable",{"type":31,"value":426}," avoids requesting a total count:",{"type":26,"tag":89,"props":428,"children":430},{"className":91,"code":429,"language":17,"meta":8,"style":8},"public interface OrderRepository\n        extends JpaRepository\u003CPurchaseOrder, Long> {\n\n    List\u003CPurchaseOrder> findByStatusOrderByCreatedAtDescIdDesc(\n        String status, Pageable pageable);\n}\n",[431],{"type":26,"tag":15,"props":432,"children":433},{"__ignoreMap":8},[434,451,488,495,522,551],{"type":26,"tag":98,"props":435,"children":436},{"class":100,"line":101},[437,441,446],{"type":26,"tag":98,"props":438,"children":439},{"style":111},[440],{"type":31,"value":163},{"type":26,"tag":98,"props":442,"children":443},{"style":111},[444],{"type":31,"value":445}," interface",{"type":26,"tag":98,"props":447,"children":448},{"style":171},[449],{"type":31,"value":450}," OrderRepository\n",{"type":26,"tag":98,"props":452,"children":453},{"class":100,"line":117},[454,459,464,469,474,478,483],{"type":26,"tag":98,"props":455,"children":456},{"style":111},[457],{"type":31,"value":458},"        extends",{"type":26,"tag":98,"props":460,"children":461},{"style":171},[462],{"type":31,"value":463}," JpaRepository",{"type":26,"tag":98,"props":465,"children":466},{"style":105},[467],{"type":31,"value":468},"\u003C",{"type":26,"tag":98,"props":470,"children":471},{"style":111},[472],{"type":31,"value":473},"PurchaseOrder",{"type":26,"tag":98,"props":475,"children":476},{"style":105},[477],{"type":31,"value":335},{"type":26,"tag":98,"props":479,"children":480},{"style":111},[481],{"type":31,"value":482},"Long",{"type":26,"tag":98,"props":484,"children":485},{"style":105},[486],{"type":31,"value":487},"> {\n",{"type":26,"tag":98,"props":489,"children":490},{"class":100,"line":157},[491],{"type":26,"tag":98,"props":492,"children":493},{"emptyLinePlaceholder":214},[494],{"type":31,"value":217},{"type":26,"tag":98,"props":496,"children":497},{"class":100,"line":182},[498,503,507,512,517],{"type":26,"tag":98,"props":499,"children":500},{"style":105},[501],{"type":31,"value":502},"    List\u003C",{"type":26,"tag":98,"props":504,"children":505},{"style":111},[506],{"type":31,"value":473},{"type":26,"tag":98,"props":508,"children":509},{"style":105},[510],{"type":31,"value":511},"> ",{"type":26,"tag":98,"props":513,"children":514},{"style":171},[515],{"type":31,"value":516},"findByStatusOrderByCreatedAtDescIdDesc",{"type":26,"tag":98,"props":518,"children":519},{"style":105},[520],{"type":31,"value":521},"(\n",{"type":26,"tag":98,"props":523,"children":524},{"class":100,"line":196},[525,530,536,541,546],{"type":26,"tag":98,"props":526,"children":527},{"style":105},[528],{"type":31,"value":529},"        String ",{"type":26,"tag":98,"props":531,"children":533},{"style":532},"--shiki-default:#FFAB70;--shiki-light:#E36209",[534],{"type":31,"value":535},"status",{"type":26,"tag":98,"props":537,"children":538},{"style":105},[539],{"type":31,"value":540},", Pageable ",{"type":26,"tag":98,"props":542,"children":543},{"style":532},[544],{"type":31,"value":545},"pageable",{"type":26,"tag":98,"props":547,"children":548},{"style":105},[549],{"type":31,"value":550},");\n",{"type":26,"tag":98,"props":552,"children":553},{"class":100,"line":210},[554],{"type":26,"tag":98,"props":555,"children":556},{"style":105},[557],{"type":31,"value":374},{"type":26,"tag":27,"props":559,"children":560},{},[561],{"type":31,"value":562},"The service maps those entities inside a read-only transaction:",{"type":26,"tag":89,"props":564,"children":566},{"className":91,"code":565,"language":17,"meta":8,"style":8},"@Transactional(readOnly = true)\npublic List\u003COrderSummary> search(String status, int page, int size) {\n    var orders = repository.findByStatusOrderByCreatedAtDescIdDesc(\n        status, PageRequest.of(page, size));\n\n    return orders.stream()\n        .map(order -> new OrderSummary(\n            order.getId(),\n            order.getCustomer().getName()))\n        .toList();\n}\n",[567],{"type":26,"tag":15,"props":568,"children":569},{"__ignoreMap":8},[570,604,659,690,708,715,738,775,793,820,837],{"type":26,"tag":98,"props":571,"children":572},{"class":100,"line":101},[573,577,582,586,591,595,600],{"type":26,"tag":98,"props":574,"children":575},{"style":105},[576],{"type":31,"value":108},{"type":26,"tag":98,"props":578,"children":579},{"style":111},[580],{"type":31,"value":581},"Transactional",{"type":26,"tag":98,"props":583,"children":584},{"style":105},[585],{"type":31,"value":132},{"type":26,"tag":98,"props":587,"children":588},{"style":135},[589],{"type":31,"value":590},"readOnly",{"type":26,"tag":98,"props":592,"children":593},{"style":111},[594],{"type":31,"value":143},{"type":26,"tag":98,"props":596,"children":597},{"style":135},[598],{"type":31,"value":599}," true",{"type":26,"tag":98,"props":601,"children":602},{"style":105},[603],{"type":31,"value":154},{"type":26,"tag":98,"props":605,"children":606},{"class":100,"line":117},[607,611,616,620,625,630,635,640,645,650,654],{"type":26,"tag":98,"props":608,"children":609},{"style":111},[610],{"type":31,"value":163},{"type":26,"tag":98,"props":612,"children":613},{"style":105},[614],{"type":31,"value":615}," List",{"type":26,"tag":98,"props":617,"children":618},{"style":111},[619],{"type":31,"value":468},{"type":26,"tag":98,"props":621,"children":622},{"style":105},[623],{"type":31,"value":624},"OrderSummary",{"type":26,"tag":98,"props":626,"children":627},{"style":111},[628],{"type":31,"value":629},">",{"type":26,"tag":98,"props":631,"children":632},{"style":171},[633],{"type":31,"value":634}," search",{"type":26,"tag":98,"props":636,"children":637},{"style":105},[638],{"type":31,"value":639},"(String status, ",{"type":26,"tag":98,"props":641,"children":642},{"style":111},[643],{"type":31,"value":644},"int",{"type":26,"tag":98,"props":646,"children":647},{"style":105},[648],{"type":31,"value":649}," page, ",{"type":26,"tag":98,"props":651,"children":652},{"style":111},[653],{"type":31,"value":644},{"type":26,"tag":98,"props":655,"children":656},{"style":105},[657],{"type":31,"value":658}," size) {\n",{"type":26,"tag":98,"props":660,"children":661},{"class":100,"line":157},[662,667,672,677,682,686],{"type":26,"tag":98,"props":663,"children":664},{"style":111},[665],{"type":31,"value":666},"    var",{"type":26,"tag":98,"props":668,"children":669},{"style":105},[670],{"type":31,"value":671}," orders ",{"type":26,"tag":98,"props":673,"children":674},{"style":111},[675],{"type":31,"value":676},"=",{"type":26,"tag":98,"props":678,"children":679},{"style":105},[680],{"type":31,"value":681}," repository.",{"type":26,"tag":98,"props":683,"children":684},{"style":171},[685],{"type":31,"value":516},{"type":26,"tag":98,"props":687,"children":688},{"style":105},[689],{"type":31,"value":521},{"type":26,"tag":98,"props":691,"children":692},{"class":100,"line":182},[693,698,703],{"type":26,"tag":98,"props":694,"children":695},{"style":105},[696],{"type":31,"value":697},"        status, PageRequest.",{"type":26,"tag":98,"props":699,"children":700},{"style":171},[701],{"type":31,"value":702},"of",{"type":26,"tag":98,"props":704,"children":705},{"style":105},[706],{"type":31,"value":707},"(page, size));\n",{"type":26,"tag":98,"props":709,"children":710},{"class":100,"line":196},[711],{"type":26,"tag":98,"props":712,"children":713},{"emptyLinePlaceholder":214},[714],{"type":31,"value":217},{"type":26,"tag":98,"props":716,"children":717},{"class":100,"line":210},[718,723,728,733],{"type":26,"tag":98,"props":719,"children":720},{"style":111},[721],{"type":31,"value":722},"    return",{"type":26,"tag":98,"props":724,"children":725},{"style":105},[726],{"type":31,"value":727}," orders.",{"type":26,"tag":98,"props":729,"children":730},{"style":171},[731],{"type":31,"value":732},"stream",{"type":26,"tag":98,"props":734,"children":735},{"style":105},[736],{"type":31,"value":737},"()\n",{"type":26,"tag":98,"props":739,"children":740},{"class":100,"line":220},[741,746,751,756,761,766,771],{"type":26,"tag":98,"props":742,"children":743},{"style":105},[744],{"type":31,"value":745},"        .",{"type":26,"tag":98,"props":747,"children":748},{"style":171},[749],{"type":31,"value":750},"map",{"type":26,"tag":98,"props":752,"children":753},{"style":105},[754],{"type":31,"value":755},"(order ",{"type":26,"tag":98,"props":757,"children":758},{"style":111},[759],{"type":31,"value":760},"->",{"type":26,"tag":98,"props":762,"children":763},{"style":111},[764],{"type":31,"value":765}," new",{"type":26,"tag":98,"props":767,"children":768},{"style":171},[769],{"type":31,"value":770}," OrderSummary",{"type":26,"tag":98,"props":772,"children":773},{"style":105},[774],{"type":31,"value":521},{"type":26,"tag":98,"props":776,"children":777},{"class":100,"line":233},[778,783,788],{"type":26,"tag":98,"props":779,"children":780},{"style":105},[781],{"type":31,"value":782},"            order.",{"type":26,"tag":98,"props":784,"children":785},{"style":171},[786],{"type":31,"value":787},"getId",{"type":26,"tag":98,"props":789,"children":790},{"style":105},[791],{"type":31,"value":792},"(),\n",{"type":26,"tag":98,"props":794,"children":795},{"class":100,"line":246},[796,800,805,810,815],{"type":26,"tag":98,"props":797,"children":798},{"style":105},[799],{"type":31,"value":782},{"type":26,"tag":98,"props":801,"children":802},{"style":171},[803],{"type":31,"value":804},"getCustomer",{"type":26,"tag":98,"props":806,"children":807},{"style":105},[808],{"type":31,"value":809},"().",{"type":26,"tag":98,"props":811,"children":812},{"style":171},[813],{"type":31,"value":814},"getName",{"type":26,"tag":98,"props":816,"children":817},{"style":105},[818],{"type":31,"value":819},"()))\n",{"type":26,"tag":98,"props":821,"children":822},{"class":100,"line":254},[823,827,832],{"type":26,"tag":98,"props":824,"children":825},{"style":105},[826],{"type":31,"value":745},{"type":26,"tag":98,"props":828,"children":829},{"style":171},[830],{"type":31,"value":831},"toList",{"type":26,"tag":98,"props":833,"children":834},{"style":105},[835],{"type":31,"value":836},"();\n",{"type":26,"tag":98,"props":838,"children":839},{"class":100,"line":303},[840],{"type":26,"tag":98,"props":841,"children":842},{"style":105},[843],{"type":31,"value":374},{"type":26,"tag":27,"props":845,"children":846},{},[847,849,854],{"type":31,"value":848},"The endpoint should validate the page number and enforce a maximum page size. ",{"type":26,"tag":15,"props":850,"children":852},{"className":851},[],[853],{"type":31,"value":624},{"type":31,"value":855}," is a DTO containing an order ID and customer name.",{"type":26,"tag":27,"props":857,"children":858},{},[859,861,867],{"type":31,"value":860},"The easy-to-miss operation is ",{"type":26,"tag":15,"props":862,"children":864},{"className":863},[],[865],{"type":31,"value":866},"getCustomer().getName()",{"type":31,"value":868},". If the customer has not been loaded, accessing its name can initialize a proxy and execute SQL. The stream looks like an in-memory transformation, but it can involve database round trips.",{"type":26,"tag":27,"props":870,"children":871},{},[872],{"type":31,"value":873},"For three orders with three different, uncached customers, the SQL can look like this. Values are substituted here for readability; real application queries should use bound parameters:",{"type":26,"tag":89,"props":875,"children":879},{"className":876,"code":877,"language":878,"meta":8,"style":8},"language-sql shiki shiki-themes github-dark github-light","select id, status, created_at, customer_id\nfrom purchase_order\nwhere status = 'OPEN'\norder by created_at desc, id desc\nlimit 3 offset 0;\n\nselect id, name from customer where id = 101;\nselect id, name from customer where id = 102;\nselect id, name from customer where id = 103;\n","sql",[880],{"type":26,"tag":15,"props":881,"children":882},{"__ignoreMap":8},[883,905,918,940,968,996,1003,1050,1094],{"type":26,"tag":98,"props":884,"children":885},{"class":100,"line":101},[886,891,896,900],{"type":26,"tag":98,"props":887,"children":888},{"style":111},[889],{"type":31,"value":890},"select",{"type":26,"tag":98,"props":892,"children":893},{"style":105},[894],{"type":31,"value":895}," id, ",{"type":26,"tag":98,"props":897,"children":898},{"style":111},[899],{"type":31,"value":535},{"type":26,"tag":98,"props":901,"children":902},{"style":105},[903],{"type":31,"value":904},", created_at, customer_id\n",{"type":26,"tag":98,"props":906,"children":907},{"class":100,"line":117},[908,913],{"type":26,"tag":98,"props":909,"children":910},{"style":111},[911],{"type":31,"value":912},"from",{"type":26,"tag":98,"props":914,"children":915},{"style":105},[916],{"type":31,"value":917}," purchase_order\n",{"type":26,"tag":98,"props":919,"children":920},{"class":100,"line":157},[921,926,931,935],{"type":26,"tag":98,"props":922,"children":923},{"style":111},[924],{"type":31,"value":925},"where",{"type":26,"tag":98,"props":927,"children":928},{"style":111},[929],{"type":31,"value":930}," status",{"type":26,"tag":98,"props":932,"children":933},{"style":111},[934],{"type":31,"value":143},{"type":26,"tag":98,"props":936,"children":937},{"style":146},[938],{"type":31,"value":939}," 'OPEN'\n",{"type":26,"tag":98,"props":941,"children":942},{"class":100,"line":182},[943,948,953,958,963],{"type":26,"tag":98,"props":944,"children":945},{"style":111},[946],{"type":31,"value":947},"order by",{"type":26,"tag":98,"props":949,"children":950},{"style":105},[951],{"type":31,"value":952}," created_at ",{"type":26,"tag":98,"props":954,"children":955},{"style":111},[956],{"type":31,"value":957},"desc",{"type":26,"tag":98,"props":959,"children":960},{"style":105},[961],{"type":31,"value":962},", id ",{"type":26,"tag":98,"props":964,"children":965},{"style":111},[966],{"type":31,"value":967},"desc\n",{"type":26,"tag":98,"props":969,"children":970},{"class":100,"line":196},[971,976,981,986,991],{"type":26,"tag":98,"props":972,"children":973},{"style":111},[974],{"type":31,"value":975},"limit",{"type":26,"tag":98,"props":977,"children":978},{"style":135},[979],{"type":31,"value":980}," 3",{"type":26,"tag":98,"props":982,"children":983},{"style":105},[984],{"type":31,"value":985}," offset ",{"type":26,"tag":98,"props":987,"children":988},{"style":135},[989],{"type":31,"value":990},"0",{"type":26,"tag":98,"props":992,"children":993},{"style":105},[994],{"type":31,"value":995},";\n",{"type":26,"tag":98,"props":997,"children":998},{"class":100,"line":210},[999],{"type":26,"tag":98,"props":1000,"children":1001},{"emptyLinePlaceholder":214},[1002],{"type":31,"value":217},{"type":26,"tag":98,"props":1004,"children":1005},{"class":100,"line":220},[1006,1010,1014,1018,1023,1028,1032,1037,1041,1046],{"type":26,"tag":98,"props":1007,"children":1008},{"style":111},[1009],{"type":31,"value":890},{"type":26,"tag":98,"props":1011,"children":1012},{"style":105},[1013],{"type":31,"value":895},{"type":26,"tag":98,"props":1015,"children":1016},{"style":111},[1017],{"type":31,"value":138},{"type":26,"tag":98,"props":1019,"children":1020},{"style":111},[1021],{"type":31,"value":1022}," from",{"type":26,"tag":98,"props":1024,"children":1025},{"style":105},[1026],{"type":31,"value":1027}," customer ",{"type":26,"tag":98,"props":1029,"children":1030},{"style":111},[1031],{"type":31,"value":925},{"type":26,"tag":98,"props":1033,"children":1034},{"style":105},[1035],{"type":31,"value":1036}," id ",{"type":26,"tag":98,"props":1038,"children":1039},{"style":111},[1040],{"type":31,"value":676},{"type":26,"tag":98,"props":1042,"children":1043},{"style":135},[1044],{"type":31,"value":1045}," 101",{"type":26,"tag":98,"props":1047,"children":1048},{"style":105},[1049],{"type":31,"value":995},{"type":26,"tag":98,"props":1051,"children":1052},{"class":100,"line":233},[1053,1057,1061,1065,1069,1073,1077,1081,1085,1090],{"type":26,"tag":98,"props":1054,"children":1055},{"style":111},[1056],{"type":31,"value":890},{"type":26,"tag":98,"props":1058,"children":1059},{"style":105},[1060],{"type":31,"value":895},{"type":26,"tag":98,"props":1062,"children":1063},{"style":111},[1064],{"type":31,"value":138},{"type":26,"tag":98,"props":1066,"children":1067},{"style":111},[1068],{"type":31,"value":1022},{"type":26,"tag":98,"props":1070,"children":1071},{"style":105},[1072],{"type":31,"value":1027},{"type":26,"tag":98,"props":1074,"children":1075},{"style":111},[1076],{"type":31,"value":925},{"type":26,"tag":98,"props":1078,"children":1079},{"style":105},[1080],{"type":31,"value":1036},{"type":26,"tag":98,"props":1082,"children":1083},{"style":111},[1084],{"type":31,"value":676},{"type":26,"tag":98,"props":1086,"children":1087},{"style":135},[1088],{"type":31,"value":1089}," 102",{"type":26,"tag":98,"props":1091,"children":1092},{"style":105},[1093],{"type":31,"value":995},{"type":26,"tag":98,"props":1095,"children":1096},{"class":100,"line":246},[1097,1101,1105,1109,1113,1117,1121,1125,1129,1134],{"type":26,"tag":98,"props":1098,"children":1099},{"style":111},[1100],{"type":31,"value":890},{"type":26,"tag":98,"props":1102,"children":1103},{"style":105},[1104],{"type":31,"value":895},{"type":26,"tag":98,"props":1106,"children":1107},{"style":111},[1108],{"type":31,"value":138},{"type":26,"tag":98,"props":1110,"children":1111},{"style":111},[1112],{"type":31,"value":1022},{"type":26,"tag":98,"props":1114,"children":1115},{"style":105},[1116],{"type":31,"value":1027},{"type":26,"tag":98,"props":1118,"children":1119},{"style":111},[1120],{"type":31,"value":925},{"type":26,"tag":98,"props":1122,"children":1123},{"style":105},[1124],{"type":31,"value":1036},{"type":26,"tag":98,"props":1126,"children":1127},{"style":111},[1128],{"type":31,"value":676},{"type":26,"tag":98,"props":1130,"children":1131},{"style":135},[1132],{"type":31,"value":1133}," 103",{"type":26,"tag":98,"props":1135,"children":1136},{"style":105},[1137],{"type":31,"value":995},{"type":26,"tag":27,"props":1139,"children":1140},{},[1141],{"type":31,"value":1142},"Shared customers and caches can reduce that count. For this single association, think of one extra lookup per distinct customer that still needs loading. Other associations can add more queries. The useful diagnostic is the pattern: database calls grow with the results being processed.",{"type":26,"tag":27,"props":1144,"children":1145},{},[1146],{"type":31,"value":1147},"N+1 is not specific to Hibernate or PostgreSQL. Explicit repository calls inside a loop can produce exactly the same behavior.",{"type":26,"tag":60,"props":1149,"children":1151},{"id":1150},"why-individually-fast-queries-can-create-a-slow-endpoint",[1152],{"type":31,"value":1153},"Why individually fast queries can create a slow endpoint",{"type":26,"tag":27,"props":1155,"children":1156},{},[1157],{"type":31,"value":1158},"Each extra query carries work beyond finding a row: a network round trip, database execution, transferring results, and application-side processing. A primary-key lookup can be efficient while dozens of sequential lookups are still expensive together.",{"type":26,"tag":27,"props":1160,"children":1161},{},[1162],{"type":31,"value":1163},"As a deliberately simplified example, 50 additional lookups taking 4 ms each from the application's perspective add about 200 ms. Those are illustrative numbers, not measurements from the anecdote. A database-only execution time would not capture all of that cost.",{"type":26,"tag":27,"props":1165,"children":1166},{},[1167],{"type":31,"value":1168},"Under concurrent traffic, those repeated calls also consume shared capacity. Requests can hold connections for longer and other requests can spend more time waiting for the pool. N+1 can contribute to latency spikes, but a spike alone does not prove it: locks, expensive queries, garbage collection, and downstream calls are other possibilities.",{"type":26,"tag":27,"props":1170,"children":1171},{},[1172,1174],{"type":31,"value":1173},"The question to investigate is concrete: ",{"type":26,"tag":47,"props":1175,"children":1176},{},[1177],{"type":31,"value":1178},"how many database statements does one search execute, and why?",{"type":26,"tag":60,"props":1180,"children":1182},{"id":1181},"detect-it-by-following-one-complete-request",[1183],{"type":31,"value":1184},"Detect it by following one complete request",{"type":26,"tag":1186,"props":1187,"children":1189},"h3",{"id":1188},"compare-small-and-larger-result-sets",[1190],{"type":31,"value":1191},"Compare small and larger result sets",{"type":26,"tag":27,"props":1193,"children":1194},{},[1195],{"type":31,"value":1196},"Use representative data in a development or test environment. Repeat the same search with page sizes such as 5, 20, and 50, ensuring there are enough matching rows. Include different customers; a fixture where every order shares one customer can hide the pattern.",{"type":26,"tag":27,"props":1198,"children":1199},{},[1200],{"type":31,"value":1201},"For the example above, with a fresh persistence context, no relevant cache hits, and batching disabled, these are the expected counts:",{"type":26,"tag":1203,"props":1204,"children":1205},"table",{},[1206,1236],{"type":26,"tag":1207,"props":1208,"children":1209},"thead",{},[1210],{"type":26,"tag":1211,"props":1212,"children":1213},"tr",{},[1214,1220,1226,1231],{"type":26,"tag":1215,"props":1216,"children":1217},"th",{},[1218],{"type":31,"value":1219},"Orders returned",{"type":26,"tag":1215,"props":1221,"children":1223},{"align":1222},"right",[1224],{"type":31,"value":1225},"Initial order query",{"type":26,"tag":1215,"props":1227,"children":1228},{"align":1222},[1229],{"type":31,"value":1230},"Customer queries",{"type":26,"tag":1215,"props":1232,"children":1233},{"align":1222},[1234],{"type":31,"value":1235},"Total",{"type":26,"tag":1237,"props":1238,"children":1239},"tbody",{},[1240,1263,1284],{"type":26,"tag":1211,"props":1241,"children":1242},{},[1243,1249,1254,1258],{"type":26,"tag":1244,"props":1245,"children":1246},"td",{},[1247],{"type":31,"value":1248},"5",{"type":26,"tag":1244,"props":1250,"children":1251},{"align":1222},[1252],{"type":31,"value":1253},"1",{"type":26,"tag":1244,"props":1255,"children":1256},{"align":1222},[1257],{"type":31,"value":1248},{"type":26,"tag":1244,"props":1259,"children":1260},{"align":1222},[1261],{"type":31,"value":1262},"6",{"type":26,"tag":1211,"props":1264,"children":1265},{},[1266,1271,1275,1279],{"type":26,"tag":1244,"props":1267,"children":1268},{},[1269],{"type":31,"value":1270},"20",{"type":26,"tag":1244,"props":1272,"children":1273},{"align":1222},[1274],{"type":31,"value":1253},{"type":26,"tag":1244,"props":1276,"children":1277},{"align":1222},[1278],{"type":31,"value":1270},{"type":26,"tag":1244,"props":1280,"children":1281},{"align":1222},[1282],{"type":31,"value":1283},"21",{"type":26,"tag":1211,"props":1285,"children":1286},{},[1287,1292,1296,1300],{"type":26,"tag":1244,"props":1288,"children":1289},{},[1290],{"type":31,"value":1291},"50",{"type":26,"tag":1244,"props":1293,"children":1294},{"align":1222},[1295],{"type":31,"value":1253},{"type":26,"tag":1244,"props":1297,"children":1298},{"align":1222},[1299],{"type":31,"value":1291},{"type":26,"tag":1244,"props":1301,"children":1302},{"align":1222},[1303],{"type":31,"value":1304},"51",{"type":26,"tag":27,"props":1306,"children":1307},{},[1308,1310,1316],{"type":31,"value":1309},"This is an explanatory model, not benchmark output. A repository returning a ",{"type":26,"tag":15,"props":1311,"children":1313},{"className":1312},[],[1314],{"type":31,"value":1315},"Page",{"type":31,"value":1317}," may also execute a count query; account for it separately. The evidence for N+1 is the repeated per-result lookup, not simply that a request executed more than one statement.",{"type":26,"tag":1186,"props":1319,"children":1321},{"id":1320},"inspect-the-sql-generated-by-that-request",[1322],{"type":31,"value":1323},"Inspect the SQL generated by that request",{"type":26,"tag":27,"props":1325,"children":1326},{},[1327],{"type":31,"value":1328},"For a Spring Boot application using Hibernate 6, a local diagnostic profile can enable SQL logging:",{"type":26,"tag":89,"props":1330,"children":1334},{"className":1331,"code":1332,"language":1333,"meta":8,"style":8},"language-yaml shiki shiki-themes github-dark github-light","logging:\n  level:\n    org.hibernate.SQL: DEBUG\n\nspring:\n  jpa:\n    properties:\n      hibernate:\n        format_sql: true\n","yaml",[1335],{"type":26,"tag":15,"props":1336,"children":1337},{"__ignoreMap":8},[1338,1352,1364,1382,1389,1401,1413,1425,1437],{"type":26,"tag":98,"props":1339,"children":1340},{"class":100,"line":101},[1341,1347],{"type":26,"tag":98,"props":1342,"children":1344},{"style":1343},"--shiki-default:#85E89D;--shiki-light:#22863A",[1345],{"type":31,"value":1346},"logging",{"type":26,"tag":98,"props":1348,"children":1349},{"style":105},[1350],{"type":31,"value":1351},":\n",{"type":26,"tag":98,"props":1353,"children":1354},{"class":100,"line":117},[1355,1360],{"type":26,"tag":98,"props":1356,"children":1357},{"style":1343},[1358],{"type":31,"value":1359},"  level",{"type":26,"tag":98,"props":1361,"children":1362},{"style":105},[1363],{"type":31,"value":1351},{"type":26,"tag":98,"props":1365,"children":1366},{"class":100,"line":157},[1367,1372,1377],{"type":26,"tag":98,"props":1368,"children":1369},{"style":1343},[1370],{"type":31,"value":1371},"    org.hibernate.SQL",{"type":26,"tag":98,"props":1373,"children":1374},{"style":105},[1375],{"type":31,"value":1376},": ",{"type":26,"tag":98,"props":1378,"children":1379},{"style":146},[1380],{"type":31,"value":1381},"DEBUG\n",{"type":26,"tag":98,"props":1383,"children":1384},{"class":100,"line":182},[1385],{"type":26,"tag":98,"props":1386,"children":1387},{"emptyLinePlaceholder":214},[1388],{"type":31,"value":217},{"type":26,"tag":98,"props":1390,"children":1391},{"class":100,"line":196},[1392,1397],{"type":26,"tag":98,"props":1393,"children":1394},{"style":1343},[1395],{"type":31,"value":1396},"spring",{"type":26,"tag":98,"props":1398,"children":1399},{"style":105},[1400],{"type":31,"value":1351},{"type":26,"tag":98,"props":1402,"children":1403},{"class":100,"line":210},[1404,1409],{"type":26,"tag":98,"props":1405,"children":1406},{"style":1343},[1407],{"type":31,"value":1408},"  jpa",{"type":26,"tag":98,"props":1410,"children":1411},{"style":105},[1412],{"type":31,"value":1351},{"type":26,"tag":98,"props":1414,"children":1415},{"class":100,"line":220},[1416,1421],{"type":26,"tag":98,"props":1417,"children":1418},{"style":1343},[1419],{"type":31,"value":1420},"    properties",{"type":26,"tag":98,"props":1422,"children":1423},{"style":105},[1424],{"type":31,"value":1351},{"type":26,"tag":98,"props":1426,"children":1427},{"class":100,"line":233},[1428,1433],{"type":26,"tag":98,"props":1429,"children":1430},{"style":1343},[1431],{"type":31,"value":1432},"      hibernate",{"type":26,"tag":98,"props":1434,"children":1435},{"style":105},[1436],{"type":31,"value":1351},{"type":26,"tag":98,"props":1438,"children":1439},{"class":100,"line":246},[1440,1445,1449],{"type":26,"tag":98,"props":1441,"children":1442},{"style":1343},[1443],{"type":31,"value":1444},"        format_sql",{"type":26,"tag":98,"props":1446,"children":1447},{"style":105},[1448],{"type":31,"value":1376},{"type":26,"tag":98,"props":1450,"children":1451},{"style":135},[1452],{"type":31,"value":1453},"true\n",{"type":26,"tag":27,"props":1455,"children":1456},{},[1457,1459,1465,1467,1473,1475,1484],{"type":31,"value":1458},"Run one request and look for the initial search followed by repeated queries with the same shape. If necessary, temporarily enable ",{"type":26,"tag":15,"props":1460,"children":1462},{"className":1461},[],[1463],{"type":31,"value":1464},"org.hibernate.orm.jdbc.bind",{"type":31,"value":1466}," at ",{"type":26,"tag":15,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":31,"value":1472},"TRACE",{"type":31,"value":1474}," to inspect parameter changes using synthetic data. Parameter logging can expose sensitive values, so keep it scoped to the investigation. These categories are documented in ",{"type":26,"tag":1476,"props":1477,"children":1481},"a",{"href":1478,"rel":1479},"https://docs.hibernate.org/orm/6.6/introduction/html_single/#logging-generated-sql",[1480],"nofollow",[1482],{"type":31,"value":1483},"Hibernate's SQL logging guide",{"type":31,"value":82},{"type":26,"tag":27,"props":1486,"children":1487},{},[1488],{"type":31,"value":1489},"With concurrent traffic, use request-correlated logs or a trace with JDBC instrumentation. Inspect database spans within one request rather than counting interleaved statements from the entire application. Check the instrumentation's sampling and span limits before assuming the trace contains every query.",{"type":26,"tag":27,"props":1491,"children":1492},{},[1493],{"type":31,"value":1494},"Follow the request through DTO mapping and serialization. A mapper, computed property, or serializer that traverses entities can trigger loads after the repository method returns. An open persistence context can allow those queries to happen surprisingly late.",{"type":26,"tag":1186,"props":1496,"children":1498},{"id":1497},"use-postgresql-to-corroborate-what-you-found",[1499],{"type":31,"value":1500},"Use PostgreSQL to corroborate what you found",{"type":26,"tag":27,"props":1502,"children":1503},{},[1504,1506,1512],{"type":31,"value":1505},"If ",{"type":26,"tag":15,"props":1507,"children":1509},{"className":1508},[],[1510],{"type":31,"value":1511},"pg_stat_statements",{"type":31,"value":1513}," is already configured and available to your database role, inspect frequently executed statements:",{"type":26,"tag":89,"props":1515,"children":1517},{"className":876,"code":1516,"language":878,"meta":8,"style":8},"select query, calls, total_exec_time, mean_exec_time, rows\nfrom pg_stat_statements\nwhere dbid = (\n    select oid from pg_database where datname = current_database()\n)\norder by calls desc\nlimit 20;\n",[1518],{"type":26,"tag":15,"props":1519,"children":1520},{"__ignoreMap":8},[1521,1538,1550,1571,1611,1618,1634],{"type":26,"tag":98,"props":1522,"children":1523},{"class":100,"line":101},[1524,1528,1533],{"type":26,"tag":98,"props":1525,"children":1526},{"style":111},[1527],{"type":31,"value":890},{"type":26,"tag":98,"props":1529,"children":1530},{"style":105},[1531],{"type":31,"value":1532}," query, calls, total_exec_time, mean_exec_time, ",{"type":26,"tag":98,"props":1534,"children":1535},{"style":111},[1536],{"type":31,"value":1537},"rows\n",{"type":26,"tag":98,"props":1539,"children":1540},{"class":100,"line":117},[1541,1545],{"type":26,"tag":98,"props":1542,"children":1543},{"style":111},[1544],{"type":31,"value":912},{"type":26,"tag":98,"props":1546,"children":1547},{"style":105},[1548],{"type":31,"value":1549}," pg_stat_statements\n",{"type":26,"tag":98,"props":1551,"children":1552},{"class":100,"line":157},[1553,1557,1562,1566],{"type":26,"tag":98,"props":1554,"children":1555},{"style":111},[1556],{"type":31,"value":925},{"type":26,"tag":98,"props":1558,"children":1559},{"style":111},[1560],{"type":31,"value":1561}," dbid",{"type":26,"tag":98,"props":1563,"children":1564},{"style":111},[1565],{"type":31,"value":143},{"type":26,"tag":98,"props":1567,"children":1568},{"style":105},[1569],{"type":31,"value":1570}," (\n",{"type":26,"tag":98,"props":1572,"children":1573},{"class":100,"line":182},[1574,1579,1584,1588,1593,1597,1602,1606],{"type":26,"tag":98,"props":1575,"children":1576},{"style":111},[1577],{"type":31,"value":1578},"    select",{"type":26,"tag":98,"props":1580,"children":1581},{"style":111},[1582],{"type":31,"value":1583}," oid",{"type":26,"tag":98,"props":1585,"children":1586},{"style":111},[1587],{"type":31,"value":1022},{"type":26,"tag":98,"props":1589,"children":1590},{"style":105},[1591],{"type":31,"value":1592}," pg_database ",{"type":26,"tag":98,"props":1594,"children":1595},{"style":111},[1596],{"type":31,"value":925},{"type":26,"tag":98,"props":1598,"children":1599},{"style":105},[1600],{"type":31,"value":1601}," datname ",{"type":26,"tag":98,"props":1603,"children":1604},{"style":111},[1605],{"type":31,"value":676},{"type":26,"tag":98,"props":1607,"children":1608},{"style":105},[1609],{"type":31,"value":1610}," current_database()\n",{"type":26,"tag":98,"props":1612,"children":1613},{"class":100,"line":196},[1614],{"type":26,"tag":98,"props":1615,"children":1616},{"style":105},[1617],{"type":31,"value":154},{"type":26,"tag":98,"props":1619,"children":1620},{"class":100,"line":210},[1621,1625,1630],{"type":26,"tag":98,"props":1622,"children":1623},{"style":111},[1624],{"type":31,"value":947},{"type":26,"tag":98,"props":1626,"children":1627},{"style":105},[1628],{"type":31,"value":1629}," calls ",{"type":26,"tag":98,"props":1631,"children":1632},{"style":111},[1633],{"type":31,"value":967},{"type":26,"tag":98,"props":1635,"children":1636},{"class":100,"line":220},[1637,1641,1646],{"type":26,"tag":98,"props":1638,"children":1639},{"style":111},[1640],{"type":31,"value":975},{"type":26,"tag":98,"props":1642,"children":1643},{"style":135},[1644],{"type":31,"value":1645}," 20",{"type":26,"tag":98,"props":1647,"children":1648},{"style":105},[1649],{"type":31,"value":995},{"type":26,"tag":27,"props":1651,"children":1652},{},[1653,1655,1662],{"type":31,"value":1654},"Look for repeated lookup shapes with high call counts. Compare counter deltas across a controlled observation window; cumulative totals alone do not identify a particular request. The extension aggregates statements across executions, so it corroborates application evidence rather than proving which endpoint caused the calls. Its execution-time fields measure database work, not full HTTP latency. See the ",{"type":26,"tag":1476,"props":1656,"children":1659},{"href":1657,"rel":1658},"https://www.postgresql.org/docs/current/pgstatstatements.html",[1480],[1660],{"type":31,"value":1661},"PostgreSQL reference",{"type":31,"value":1663}," for setup, permissions, and field definitions.",{"type":26,"tag":27,"props":1665,"children":1666},{},[1667],{"type":31,"value":1668},"For an individual lookup, inspect its plan with representative values:",{"type":26,"tag":89,"props":1670,"children":1672},{"className":876,"code":1671,"language":878,"meta":8,"style":8},"EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name FROM customer WHERE id = 101;\n",[1673],{"type":26,"tag":15,"props":1674,"children":1675},{"__ignoreMap":8},[1676,1684],{"type":26,"tag":98,"props":1677,"children":1678},{"class":100,"line":101},[1679],{"type":26,"tag":98,"props":1680,"children":1681},{"style":105},[1682],{"type":31,"value":1683},"EXPLAIN (ANALYZE, BUFFERS)\n",{"type":26,"tag":98,"props":1685,"children":1686},{"class":100,"line":117},[1687,1692,1696,1700,1705,1709,1714,1718,1722,1726],{"type":26,"tag":98,"props":1688,"children":1689},{"style":111},[1690],{"type":31,"value":1691},"SELECT",{"type":26,"tag":98,"props":1693,"children":1694},{"style":105},[1695],{"type":31,"value":895},{"type":26,"tag":98,"props":1697,"children":1698},{"style":111},[1699],{"type":31,"value":138},{"type":26,"tag":98,"props":1701,"children":1702},{"style":111},[1703],{"type":31,"value":1704}," FROM",{"type":26,"tag":98,"props":1706,"children":1707},{"style":105},[1708],{"type":31,"value":1027},{"type":26,"tag":98,"props":1710,"children":1711},{"style":111},[1712],{"type":31,"value":1713},"WHERE",{"type":26,"tag":98,"props":1715,"children":1716},{"style":105},[1717],{"type":31,"value":1036},{"type":26,"tag":98,"props":1719,"children":1720},{"style":111},[1721],{"type":31,"value":676},{"type":26,"tag":98,"props":1723,"children":1724},{"style":135},[1725],{"type":31,"value":1045},{"type":26,"tag":98,"props":1727,"children":1728},{"style":105},[1729],{"type":31,"value":995},{"type":26,"tag":27,"props":1731,"children":1732},{},[1733,1739,1741,1748],{"type":26,"tag":15,"props":1734,"children":1736},{"className":1735},[],[1737],{"type":31,"value":1738},"ANALYZE",{"type":31,"value":1740}," executes the statement, so use a suitable environment and workload. It can reveal an inefficient lookup, but even an excellent plan cannot show that the application unnecessarily executed similar queries 50 times. ",{"type":26,"tag":1476,"props":1742,"children":1745},{"href":1743,"rel":1744},"https://www.postgresql.org/docs/current/using-explain.html",[1480],[1746],{"type":31,"value":1747},"PostgreSQL's EXPLAIN guide",{"type":31,"value":1749}," explains how to interpret that plan.",{"type":26,"tag":60,"props":1751,"children":1753},{"id":1752},"choose-a-solution-around-the-response-you-need",[1754],{"type":31,"value":1755},"Choose a solution around the response you need",{"type":26,"tag":27,"props":1757,"children":1758},{},[1759],{"type":31,"value":1760},"Before changing annotations, write down what the endpoint must return. A search summary, an entity used for business logic, and a nested response with collections need different fetching decisions.",{"type":26,"tag":1186,"props":1762,"children":1764},{"id":1763},"return-a-dto-projection-for-a-search-summary",[1765],{"type":31,"value":1766},"Return a DTO projection for a search summary",{"type":26,"tag":27,"props":1768,"children":1769},{},[1770],{"type":31,"value":1771},"If the response only needs the order ID and customer name, query those values directly. Define the record in its own file:",{"type":26,"tag":89,"props":1773,"children":1775},{"className":91,"code":1774,"language":17,"meta":8,"style":8},"package com.example.orders;\n\npublic record OrderSummary(Long id, String customerName) {}\n",[1776],{"type":26,"tag":15,"props":1777,"children":1778},{"__ignoreMap":8},[1779,1792,1799],{"type":26,"tag":98,"props":1780,"children":1781},{"class":100,"line":101},[1782,1787],{"type":26,"tag":98,"props":1783,"children":1784},{"style":111},[1785],{"type":31,"value":1786},"package",{"type":26,"tag":98,"props":1788,"children":1789},{"style":105},[1790],{"type":31,"value":1791}," com.example.orders;\n",{"type":26,"tag":98,"props":1793,"children":1794},{"class":100,"line":117},[1795],{"type":26,"tag":98,"props":1796,"children":1797},{"emptyLinePlaceholder":214},[1798],{"type":31,"value":217},{"type":26,"tag":98,"props":1800,"children":1801},{"class":100,"line":157},[1802,1806,1811,1815],{"type":26,"tag":98,"props":1803,"children":1804},{"style":111},[1805],{"type":31,"value":163},{"type":26,"tag":98,"props":1807,"children":1808},{"style":111},[1809],{"type":31,"value":1810}," record",{"type":26,"tag":98,"props":1812,"children":1813},{"style":171},[1814],{"type":31,"value":770},{"type":26,"tag":98,"props":1816,"children":1817},{"style":105},[1818],{"type":31,"value":1819},"(Long id, String customerName) {}\n",{"type":26,"tag":27,"props":1821,"children":1822},{},[1823],{"type":31,"value":1824},"Then replace the entity query with an explicit constructor projection:",{"type":26,"tag":89,"props":1826,"children":1828},{"className":91,"code":1827,"language":17,"meta":8,"style":8},"@Query(\"\"\"\n    select new com.example.orders.OrderSummary(o.id, c.name)\n    from PurchaseOrder o\n    join o.customer c\n    where o.status = :status\n    order by o.createdAt desc, o.id desc\n    \"\"\")\nList\u003COrderSummary> searchSummaries(\n    @Param(\"status\") String status, Pageable pageable);\n",[1829],{"type":26,"tag":15,"props":1830,"children":1831},{"__ignoreMap":8},[1832,1853,1861,1869,1877,1885,1893,1905,1933],{"type":26,"tag":98,"props":1833,"children":1834},{"class":100,"line":101},[1835,1839,1844,1848],{"type":26,"tag":98,"props":1836,"children":1837},{"style":105},[1838],{"type":31,"value":108},{"type":26,"tag":98,"props":1840,"children":1841},{"style":111},[1842],{"type":31,"value":1843},"Query",{"type":26,"tag":98,"props":1845,"children":1846},{"style":105},[1847],{"type":31,"value":132},{"type":26,"tag":98,"props":1849,"children":1850},{"style":146},[1851],{"type":31,"value":1852},"\"\"\"\n",{"type":26,"tag":98,"props":1854,"children":1855},{"class":100,"line":117},[1856],{"type":26,"tag":98,"props":1857,"children":1858},{"style":146},[1859],{"type":31,"value":1860},"    select new com.example.orders.OrderSummary(o.id, c.name)\n",{"type":26,"tag":98,"props":1862,"children":1863},{"class":100,"line":157},[1864],{"type":26,"tag":98,"props":1865,"children":1866},{"style":146},[1867],{"type":31,"value":1868},"    from PurchaseOrder o\n",{"type":26,"tag":98,"props":1870,"children":1871},{"class":100,"line":182},[1872],{"type":26,"tag":98,"props":1873,"children":1874},{"style":146},[1875],{"type":31,"value":1876},"    join o.customer c\n",{"type":26,"tag":98,"props":1878,"children":1879},{"class":100,"line":196},[1880],{"type":26,"tag":98,"props":1881,"children":1882},{"style":146},[1883],{"type":31,"value":1884},"    where o.status = :status\n",{"type":26,"tag":98,"props":1886,"children":1887},{"class":100,"line":210},[1888],{"type":26,"tag":98,"props":1889,"children":1890},{"style":146},[1891],{"type":31,"value":1892},"    order by o.createdAt desc, o.id desc\n",{"type":26,"tag":98,"props":1894,"children":1895},{"class":100,"line":220},[1896,1901],{"type":26,"tag":98,"props":1897,"children":1898},{"style":146},[1899],{"type":31,"value":1900},"    \"\"\"",{"type":26,"tag":98,"props":1902,"children":1903},{"style":105},[1904],{"type":31,"value":154},{"type":26,"tag":98,"props":1906,"children":1907},{"class":100,"line":233},[1908,1912,1916,1920,1924,1929],{"type":26,"tag":98,"props":1909,"children":1910},{"style":105},[1911],{"type":31,"value":416},{"type":26,"tag":98,"props":1913,"children":1914},{"style":111},[1915],{"type":31,"value":468},{"type":26,"tag":98,"props":1917,"children":1918},{"style":105},[1919],{"type":31,"value":624},{"type":26,"tag":98,"props":1921,"children":1922},{"style":111},[1923],{"type":31,"value":629},{"type":26,"tag":98,"props":1925,"children":1926},{"style":171},[1927],{"type":31,"value":1928}," searchSummaries",{"type":26,"tag":98,"props":1930,"children":1931},{"style":105},[1932],{"type":31,"value":521},{"type":26,"tag":98,"props":1934,"children":1935},{"class":100,"line":246},[1936,1940,1945,1949,1954],{"type":26,"tag":98,"props":1937,"children":1938},{"style":105},[1939],{"type":31,"value":188},{"type":26,"tag":98,"props":1941,"children":1942},{"style":111},[1943],{"type":31,"value":1944},"Param",{"type":26,"tag":98,"props":1946,"children":1947},{"style":105},[1948],{"type":31,"value":132},{"type":26,"tag":98,"props":1950,"children":1951},{"style":146},[1952],{"type":31,"value":1953},"\"status\"",{"type":26,"tag":98,"props":1955,"children":1956},{"style":105},[1957],{"type":31,"value":1958},") String status, Pageable pageable);\n",{"type":26,"tag":27,"props":1960,"children":1961},{},[1962],{"type":31,"value":1963},"The service returns this result without traversing customer entities. In this example, one bounded query provides the summary fields. The mandatory customer relationship makes the inner join appropriate; optional relationships need deliberate handling, often a left join.",{"type":26,"tag":27,"props":1965,"children":1966},{},[1967,1969,1976],{"type":31,"value":1968},"Explicit scalar DTOs make the read contract visible. Be careful when replacing them with nested projections or projections that contain entities: the fetching behavior may change. ",{"type":26,"tag":1476,"props":1970,"children":1973},{"href":1971,"rel":1972},"https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html",[1480],[1974],{"type":31,"value":1975},"Spring Data's projection documentation",{"type":31,"value":1977}," covers those distinctions.",{"type":26,"tag":1186,"props":1979,"children":1981},{"id":1980},"fetch-a-required-association-with-the-entities",[1982],{"type":31,"value":1983},"Fetch a required association with the entities",{"type":26,"tag":27,"props":1985,"children":1986},{},[1987],{"type":31,"value":1988},"When the service needs managed orders and their customers, declare that requirement in the query:",{"type":26,"tag":89,"props":1990,"children":1992},{"className":91,"code":1991,"language":17,"meta":8,"style":8},"@Query(\"\"\"\n    select o\n    from PurchaseOrder o\n    join fetch o.customer\n    where o.status = :status\n    order by o.createdAt desc, o.id desc\n    \"\"\")\nList\u003CPurchaseOrder> searchWithCustomers(\n    @Param(\"status\") String status, Pageable pageable);\n",[1993],{"type":26,"tag":15,"props":1994,"children":1995},{"__ignoreMap":8},[1996,2015,2023,2030,2038,2045,2052,2063,2091],{"type":26,"tag":98,"props":1997,"children":1998},{"class":100,"line":101},[1999,2003,2007,2011],{"type":26,"tag":98,"props":2000,"children":2001},{"style":105},[2002],{"type":31,"value":108},{"type":26,"tag":98,"props":2004,"children":2005},{"style":111},[2006],{"type":31,"value":1843},{"type":26,"tag":98,"props":2008,"children":2009},{"style":105},[2010],{"type":31,"value":132},{"type":26,"tag":98,"props":2012,"children":2013},{"style":146},[2014],{"type":31,"value":1852},{"type":26,"tag":98,"props":2016,"children":2017},{"class":100,"line":117},[2018],{"type":26,"tag":98,"props":2019,"children":2020},{"style":146},[2021],{"type":31,"value":2022},"    select o\n",{"type":26,"tag":98,"props":2024,"children":2025},{"class":100,"line":157},[2026],{"type":26,"tag":98,"props":2027,"children":2028},{"style":146},[2029],{"type":31,"value":1868},{"type":26,"tag":98,"props":2031,"children":2032},{"class":100,"line":182},[2033],{"type":26,"tag":98,"props":2034,"children":2035},{"style":146},[2036],{"type":31,"value":2037},"    join fetch o.customer\n",{"type":26,"tag":98,"props":2039,"children":2040},{"class":100,"line":196},[2041],{"type":26,"tag":98,"props":2042,"children":2043},{"style":146},[2044],{"type":31,"value":1884},{"type":26,"tag":98,"props":2046,"children":2047},{"class":100,"line":210},[2048],{"type":26,"tag":98,"props":2049,"children":2050},{"style":146},[2051],{"type":31,"value":1892},{"type":26,"tag":98,"props":2053,"children":2054},{"class":100,"line":220},[2055,2059],{"type":26,"tag":98,"props":2056,"children":2057},{"style":146},[2058],{"type":31,"value":1900},{"type":26,"tag":98,"props":2060,"children":2061},{"style":105},[2062],{"type":31,"value":154},{"type":26,"tag":98,"props":2064,"children":2065},{"class":100,"line":233},[2066,2070,2074,2078,2082,2087],{"type":26,"tag":98,"props":2067,"children":2068},{"style":105},[2069],{"type":31,"value":416},{"type":26,"tag":98,"props":2071,"children":2072},{"style":111},[2073],{"type":31,"value":468},{"type":26,"tag":98,"props":2075,"children":2076},{"style":105},[2077],{"type":31,"value":473},{"type":26,"tag":98,"props":2079,"children":2080},{"style":111},[2081],{"type":31,"value":629},{"type":26,"tag":98,"props":2083,"children":2084},{"style":171},[2085],{"type":31,"value":2086}," searchWithCustomers",{"type":26,"tag":98,"props":2088,"children":2089},{"style":105},[2090],{"type":31,"value":521},{"type":26,"tag":98,"props":2092,"children":2093},{"class":100,"line":246},[2094,2098,2102,2106,2110],{"type":26,"tag":98,"props":2095,"children":2096},{"style":105},[2097],{"type":31,"value":188},{"type":26,"tag":98,"props":2099,"children":2100},{"style":111},[2101],{"type":31,"value":1944},{"type":26,"tag":98,"props":2103,"children":2104},{"style":105},[2105],{"type":31,"value":132},{"type":26,"tag":98,"props":2107,"children":2108},{"style":146},[2109],{"type":31,"value":1953},{"type":26,"tag":98,"props":2111,"children":2112},{"style":105},[2113],{"type":31,"value":1958},{"type":26,"tag":27,"props":2115,"children":2116},{},[2117,2119,2124,2126,2133],{"type":31,"value":2118},"This fetches a ",{"type":26,"tag":47,"props":2120,"children":2121},{},[2122],{"type":31,"value":2123},"to-one",{"type":31,"value":2125}," relationship: each order has one customer, so that join does not multiply order rows. Mapping the customer name no longer requires a separate customer lookup. Verify other accessed associations as well. The ",{"type":26,"tag":1476,"props":2127,"children":2130},{"href":2128,"rel":2129},"https://docs.hibernate.org/orm/6.6/querylanguage/html_single/#join-fetch",[1480],[2131],{"type":31,"value":2132},"Hibernate query guide",{"type":31,"value":2134}," describes fetch joins and their limitations.",{"type":26,"tag":27,"props":2136,"children":2137},{},[2138],{"type":31,"value":2139},"An alternative for the original repository method is a fetch graph:",{"type":26,"tag":89,"props":2141,"children":2143},{"className":91,"code":2142,"language":17,"meta":8,"style":8},"@EntityGraph(attributePaths = \"customer\")\nList\u003CPurchaseOrder> findByStatusOrderByCreatedAtDescIdDesc(\n    String status, Pageable pageable);\n",[2144],{"type":26,"tag":15,"props":2145,"children":2146},{"__ignoreMap":8},[2147,2181,2209],{"type":26,"tag":98,"props":2148,"children":2149},{"class":100,"line":101},[2150,2154,2159,2163,2168,2172,2177],{"type":26,"tag":98,"props":2151,"children":2152},{"style":105},[2153],{"type":31,"value":108},{"type":26,"tag":98,"props":2155,"children":2156},{"style":111},[2157],{"type":31,"value":2158},"EntityGraph",{"type":26,"tag":98,"props":2160,"children":2161},{"style":105},[2162],{"type":31,"value":132},{"type":26,"tag":98,"props":2164,"children":2165},{"style":135},[2166],{"type":31,"value":2167},"attributePaths",{"type":26,"tag":98,"props":2169,"children":2170},{"style":111},[2171],{"type":31,"value":143},{"type":26,"tag":98,"props":2173,"children":2174},{"style":146},[2175],{"type":31,"value":2176}," \"customer\"",{"type":26,"tag":98,"props":2178,"children":2179},{"style":105},[2180],{"type":31,"value":154},{"type":26,"tag":98,"props":2182,"children":2183},{"class":100,"line":117},[2184,2188,2192,2196,2200,2205],{"type":26,"tag":98,"props":2185,"children":2186},{"style":105},[2187],{"type":31,"value":416},{"type":26,"tag":98,"props":2189,"children":2190},{"style":111},[2191],{"type":31,"value":468},{"type":26,"tag":98,"props":2193,"children":2194},{"style":105},[2195],{"type":31,"value":473},{"type":26,"tag":98,"props":2197,"children":2198},{"style":111},[2199],{"type":31,"value":629},{"type":26,"tag":98,"props":2201,"children":2202},{"style":171},[2203],{"type":31,"value":2204}," findByStatusOrderByCreatedAtDescIdDesc",{"type":26,"tag":98,"props":2206,"children":2207},{"style":105},[2208],{"type":31,"value":521},{"type":26,"tag":98,"props":2210,"children":2211},{"class":100,"line":157},[2212],{"type":26,"tag":98,"props":2213,"children":2214},{"style":105},[2215],{"type":31,"value":2216},"    String status, Pageable pageable);\n",{"type":26,"tag":27,"props":2218,"children":2219},{},[2220,2222,2229],{"type":31,"value":2221},"An entity graph declares which attributes to fetch. Inspect the generated SQL rather than treating it as a universal guarantee of one statement; see ",{"type":26,"tag":1476,"props":2223,"children":2226},{"href":2224,"rel":2225},"https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.entity-graph",[1480],[2227],{"type":31,"value":2228},"Spring Data's entity graph support",{"type":31,"value":82},{"type":26,"tag":27,"props":2231,"children":2232},{},[2233,2235,2241],{"type":31,"value":2234},"Changing every association to ",{"type":26,"tag":15,"props":2236,"children":2238},{"className":2237},[],[2239],{"type":31,"value":2240},"EAGER",{"type":31,"value":2242}," is not a reliable fix. Eager loading can still use secondary selects and it changes the loading requirements for other use cases too. Make the fetching choice where the endpoint's needs are known.",{"type":26,"tag":1186,"props":2244,"children":2246},{"id":2245},"batch-association-loads-when-a-join-is-unsuitable",[2247],{"type":31,"value":2248},"Batch association loads when a join is unsuitable",{"type":26,"tag":27,"props":2250,"children":2251},{},[2252],{"type":31,"value":2253},"Hibernate can load several pending associations together. One way to enable this is:",{"type":26,"tag":89,"props":2255,"children":2257},{"className":1331,"code":2256,"language":1333,"meta":8,"style":8},"spring:\n  jpa:\n    properties:\n      hibernate:\n        default_batch_fetch_size: 32\n",[2258],{"type":26,"tag":15,"props":2259,"children":2260},{"__ignoreMap":8},[2261,2272,2283,2294,2305],{"type":26,"tag":98,"props":2262,"children":2263},{"class":100,"line":101},[2264,2268],{"type":26,"tag":98,"props":2265,"children":2266},{"style":1343},[2267],{"type":31,"value":1396},{"type":26,"tag":98,"props":2269,"children":2270},{"style":105},[2271],{"type":31,"value":1351},{"type":26,"tag":98,"props":2273,"children":2274},{"class":100,"line":117},[2275,2279],{"type":26,"tag":98,"props":2276,"children":2277},{"style":1343},[2278],{"type":31,"value":1408},{"type":26,"tag":98,"props":2280,"children":2281},{"style":105},[2282],{"type":31,"value":1351},{"type":26,"tag":98,"props":2284,"children":2285},{"class":100,"line":157},[2286,2290],{"type":26,"tag":98,"props":2287,"children":2288},{"style":1343},[2289],{"type":31,"value":1420},{"type":26,"tag":98,"props":2291,"children":2292},{"style":105},[2293],{"type":31,"value":1351},{"type":26,"tag":98,"props":2295,"children":2296},{"class":100,"line":182},[2297,2301],{"type":26,"tag":98,"props":2298,"children":2299},{"style":1343},[2300],{"type":31,"value":1432},{"type":26,"tag":98,"props":2302,"children":2303},{"style":105},[2304],{"type":31,"value":1351},{"type":26,"tag":98,"props":2306,"children":2307},{"class":100,"line":196},[2308,2313,2317],{"type":26,"tag":98,"props":2309,"children":2310},{"style":1343},[2311],{"type":31,"value":2312},"        default_batch_fetch_size",{"type":26,"tag":98,"props":2314,"children":2315},{"style":105},[2316],{"type":31,"value":1376},{"type":26,"tag":98,"props":2318,"children":2319},{"style":135},[2320],{"type":31,"value":2321},"32\n",{"type":26,"tag":27,"props":2323,"children":2324},{},[2325],{"type":31,"value":2326},"For 50 distinct uncached customers already referenced in one persistence context, a batch size of 32 might reduce the customer lookups to two batches. Including the order query, that would be three statements. Treat this as an expected shape to verify, not a guaranteed count: access order, mappings, caches, and eligible proxies affect batching.",{"type":26,"tag":27,"props":2328,"children":2329},{},[2330,2332,2338,2340,2347],{"type":31,"value":2331},"Batch fetching still materializes entities and needs tuning. It can be useful when changing the fetch query is awkward or when fetching collections separately avoids excessive row duplication. It is different from ",{"type":26,"tag":15,"props":2333,"children":2335},{"className":2334},[],[2336],{"type":31,"value":2337},"hibernate.jdbc.batch_size",{"type":31,"value":2339},", which concerns batching writes. Hibernate documents the distinction in its ",{"type":26,"tag":1476,"props":2341,"children":2344},{"href":2342,"rel":2343},"https://docs.hibernate.org/orm/6.6/userguide/html_single/#fetching-batch",[1480],[2345],{"type":31,"value":2346},"fetching guide",{"type":31,"value":82},{"type":26,"tag":27,"props":2349,"children":2350},{},[2351],{"type":31,"value":2352},"You can also make batching explicit: collect the required IDs, issue a bounded bulk query, and assemble the response from a map. That approach works without relying on ORM proxy loading.",{"type":26,"tag":60,"props":2354,"children":2356},{"id":2355},"be-especially-careful-with-collections-and-pagination",[2357],{"type":31,"value":2358},"Be especially careful with collections and pagination",{"type":26,"tag":27,"props":2360,"children":2361},{},[2362],{"type":31,"value":2363},"Suppose the search now needs order line items. Joining an order to ten items produces ten SQL rows for that order. Applying a row limit to that result is not the same as selecting a page of orders.",{"type":26,"tag":27,"props":2365,"children":2366},{},[2367,2369,2375],{"type":31,"value":2368},"Hibernate may handle a collection fetch join with pagination by loading all matching rows and paginating in memory. Multiple collection joins can also multiply the result set. A lower statement count is not automatically a faster query. The ",{"type":26,"tag":1476,"props":2370,"children":2372},{"href":2128,"rel":2371},[1480],[2373],{"type":31,"value":2374},"fetch join documentation",{"type":31,"value":2376}," calls out these restrictions.",{"type":26,"tag":27,"props":2378,"children":2379},{},[2380],{"type":31,"value":2381},"For Hibernate, enable this guard to fail when collection fetching would require in-memory pagination:",{"type":26,"tag":89,"props":2383,"children":2385},{"className":1331,"code":2384,"language":1333,"meta":8,"style":8},"spring:\n  jpa:\n    properties:\n      hibernate:\n        query:\n          fail_on_pagination_over_collection_fetch: true\n",[2386],{"type":26,"tag":15,"props":2387,"children":2388},{"__ignoreMap":8},[2389,2400,2411,2422,2433,2445],{"type":26,"tag":98,"props":2390,"children":2391},{"class":100,"line":101},[2392,2396],{"type":26,"tag":98,"props":2393,"children":2394},{"style":1343},[2395],{"type":31,"value":1396},{"type":26,"tag":98,"props":2397,"children":2398},{"style":105},[2399],{"type":31,"value":1351},{"type":26,"tag":98,"props":2401,"children":2402},{"class":100,"line":117},[2403,2407],{"type":26,"tag":98,"props":2404,"children":2405},{"style":1343},[2406],{"type":31,"value":1408},{"type":26,"tag":98,"props":2408,"children":2409},{"style":105},[2410],{"type":31,"value":1351},{"type":26,"tag":98,"props":2412,"children":2413},{"class":100,"line":157},[2414,2418],{"type":26,"tag":98,"props":2415,"children":2416},{"style":1343},[2417],{"type":31,"value":1420},{"type":26,"tag":98,"props":2419,"children":2420},{"style":105},[2421],{"type":31,"value":1351},{"type":26,"tag":98,"props":2423,"children":2424},{"class":100,"line":182},[2425,2429],{"type":26,"tag":98,"props":2426,"children":2427},{"style":1343},[2428],{"type":31,"value":1432},{"type":26,"tag":98,"props":2430,"children":2431},{"style":105},[2432],{"type":31,"value":1351},{"type":26,"tag":98,"props":2434,"children":2435},{"class":100,"line":196},[2436,2441],{"type":26,"tag":98,"props":2437,"children":2438},{"style":1343},[2439],{"type":31,"value":2440},"        query",{"type":26,"tag":98,"props":2442,"children":2443},{"style":105},[2444],{"type":31,"value":1351},{"type":26,"tag":98,"props":2446,"children":2447},{"class":100,"line":210},[2448,2453,2457],{"type":26,"tag":98,"props":2449,"children":2450},{"style":1343},[2451],{"type":31,"value":2452},"          fail_on_pagination_over_collection_fetch",{"type":26,"tag":98,"props":2454,"children":2455},{"style":105},[2456],{"type":31,"value":1376},{"type":26,"tag":98,"props":2458,"children":2459},{"style":135},[2460],{"type":31,"value":1453},{"type":26,"tag":27,"props":2462,"children":2463},{},[2464,2466,2473],{"type":31,"value":2465},"The setting's behavior is described in ",{"type":26,"tag":1476,"props":2467,"children":2470},{"href":2468,"rel":2469},"https://docs.hibernate.org/orm/6.6/javadocs/org/hibernate/cfg/QuerySettings.html#FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH",[1480],[2471],{"type":31,"value":2472},"Hibernate's query settings",{"type":31,"value":82},{"type":26,"tag":27,"props":2475,"children":2476},{},[2477],{"type":31,"value":2478},"A practical alternative is to page the parent IDs first, then fetch the data for that bounded set:",{"type":26,"tag":89,"props":2480,"children":2482},{"className":876,"code":2481,"language":878,"meta":8,"style":8},"-- First: choose the page of orders.\nselect id\nfrom purchase_order\nwhere status = 'OPEN'\norder by created_at desc, id desc\nlimit 20 offset 0;\n\n-- Second: fetch details only for the IDs returned above.\n-- The IDs below are illustrative bound-parameter values.\nselect o.id, c.name, i.id as item_id, i.quantity\nfrom purchase_order o\njoin customer c on c.id = o.customer_id\nleft join order_item i on i.order_id = o.id\nwhere o.id in (101, 102, 103)\norder by o.created_at desc, o.id desc, i.id;\n",[2483],{"type":26,"tag":15,"props":2484,"children":2485},{"__ignoreMap":8},[2486,2495,2507,2518,2537,2560,2583,2590,2598,2606,2683,2695,2743,2791,2848],{"type":26,"tag":98,"props":2487,"children":2488},{"class":100,"line":101},[2489],{"type":26,"tag":98,"props":2490,"children":2492},{"style":2491},"--shiki-default:#6A737D;--shiki-light:#6A737D",[2493],{"type":31,"value":2494},"-- First: choose the page of orders.\n",{"type":26,"tag":98,"props":2496,"children":2497},{"class":100,"line":117},[2498,2502],{"type":26,"tag":98,"props":2499,"children":2500},{"style":111},[2501],{"type":31,"value":890},{"type":26,"tag":98,"props":2503,"children":2504},{"style":105},[2505],{"type":31,"value":2506}," id\n",{"type":26,"tag":98,"props":2508,"children":2509},{"class":100,"line":157},[2510,2514],{"type":26,"tag":98,"props":2511,"children":2512},{"style":111},[2513],{"type":31,"value":912},{"type":26,"tag":98,"props":2515,"children":2516},{"style":105},[2517],{"type":31,"value":917},{"type":26,"tag":98,"props":2519,"children":2520},{"class":100,"line":182},[2521,2525,2529,2533],{"type":26,"tag":98,"props":2522,"children":2523},{"style":111},[2524],{"type":31,"value":925},{"type":26,"tag":98,"props":2526,"children":2527},{"style":111},[2528],{"type":31,"value":930},{"type":26,"tag":98,"props":2530,"children":2531},{"style":111},[2532],{"type":31,"value":143},{"type":26,"tag":98,"props":2534,"children":2535},{"style":146},[2536],{"type":31,"value":939},{"type":26,"tag":98,"props":2538,"children":2539},{"class":100,"line":196},[2540,2544,2548,2552,2556],{"type":26,"tag":98,"props":2541,"children":2542},{"style":111},[2543],{"type":31,"value":947},{"type":26,"tag":98,"props":2545,"children":2546},{"style":105},[2547],{"type":31,"value":952},{"type":26,"tag":98,"props":2549,"children":2550},{"style":111},[2551],{"type":31,"value":957},{"type":26,"tag":98,"props":2553,"children":2554},{"style":105},[2555],{"type":31,"value":962},{"type":26,"tag":98,"props":2557,"children":2558},{"style":111},[2559],{"type":31,"value":967},{"type":26,"tag":98,"props":2561,"children":2562},{"class":100,"line":210},[2563,2567,2571,2575,2579],{"type":26,"tag":98,"props":2564,"children":2565},{"style":111},[2566],{"type":31,"value":975},{"type":26,"tag":98,"props":2568,"children":2569},{"style":135},[2570],{"type":31,"value":1645},{"type":26,"tag":98,"props":2572,"children":2573},{"style":105},[2574],{"type":31,"value":985},{"type":26,"tag":98,"props":2576,"children":2577},{"style":135},[2578],{"type":31,"value":990},{"type":26,"tag":98,"props":2580,"children":2581},{"style":105},[2582],{"type":31,"value":995},{"type":26,"tag":98,"props":2584,"children":2585},{"class":100,"line":220},[2586],{"type":26,"tag":98,"props":2587,"children":2588},{"emptyLinePlaceholder":214},[2589],{"type":31,"value":217},{"type":26,"tag":98,"props":2591,"children":2592},{"class":100,"line":233},[2593],{"type":26,"tag":98,"props":2594,"children":2595},{"style":2491},[2596],{"type":31,"value":2597},"-- Second: fetch details only for the IDs returned above.\n",{"type":26,"tag":98,"props":2599,"children":2600},{"class":100,"line":246},[2601],{"type":26,"tag":98,"props":2602,"children":2603},{"style":2491},[2604],{"type":31,"value":2605},"-- The IDs below are illustrative bound-parameter values.\n",{"type":26,"tag":98,"props":2607,"children":2608},{"class":100,"line":254},[2609,2613,2618,2622,2626,2630,2635,2639,2643,2647,2652,2656,2660,2665,2670,2674,2678],{"type":26,"tag":98,"props":2610,"children":2611},{"style":111},[2612],{"type":31,"value":890},{"type":26,"tag":98,"props":2614,"children":2615},{"style":135},[2616],{"type":31,"value":2617}," o",{"type":26,"tag":98,"props":2619,"children":2620},{"style":105},[2621],{"type":31,"value":82},{"type":26,"tag":98,"props":2623,"children":2624},{"style":135},[2625],{"type":31,"value":401},{"type":26,"tag":98,"props":2627,"children":2628},{"style":105},[2629],{"type":31,"value":335},{"type":26,"tag":98,"props":2631,"children":2632},{"style":135},[2633],{"type":31,"value":2634},"c",{"type":26,"tag":98,"props":2636,"children":2637},{"style":105},[2638],{"type":31,"value":82},{"type":26,"tag":98,"props":2640,"children":2641},{"style":135},[2642],{"type":31,"value":138},{"type":26,"tag":98,"props":2644,"children":2645},{"style":105},[2646],{"type":31,"value":335},{"type":26,"tag":98,"props":2648,"children":2649},{"style":135},[2650],{"type":31,"value":2651},"i",{"type":26,"tag":98,"props":2653,"children":2654},{"style":105},[2655],{"type":31,"value":82},{"type":26,"tag":98,"props":2657,"children":2658},{"style":135},[2659],{"type":31,"value":401},{"type":26,"tag":98,"props":2661,"children":2662},{"style":111},[2663],{"type":31,"value":2664}," as",{"type":26,"tag":98,"props":2666,"children":2667},{"style":105},[2668],{"type":31,"value":2669}," item_id, ",{"type":26,"tag":98,"props":2671,"children":2672},{"style":135},[2673],{"type":31,"value":2651},{"type":26,"tag":98,"props":2675,"children":2676},{"style":105},[2677],{"type":31,"value":82},{"type":26,"tag":98,"props":2679,"children":2680},{"style":135},[2681],{"type":31,"value":2682},"quantity\n",{"type":26,"tag":98,"props":2684,"children":2685},{"class":100,"line":303},[2686,2690],{"type":26,"tag":98,"props":2687,"children":2688},{"style":111},[2689],{"type":31,"value":912},{"type":26,"tag":98,"props":2691,"children":2692},{"style":105},[2693],{"type":31,"value":2694}," purchase_order o\n",{"type":26,"tag":98,"props":2696,"children":2697},{"class":100,"line":355},[2698,2703,2708,2713,2718,2722,2726,2730,2734,2738],{"type":26,"tag":98,"props":2699,"children":2700},{"style":111},[2701],{"type":31,"value":2702},"join",{"type":26,"tag":98,"props":2704,"children":2705},{"style":105},[2706],{"type":31,"value":2707}," customer c ",{"type":26,"tag":98,"props":2709,"children":2710},{"style":111},[2711],{"type":31,"value":2712},"on",{"type":26,"tag":98,"props":2714,"children":2715},{"style":135},[2716],{"type":31,"value":2717}," c",{"type":26,"tag":98,"props":2719,"children":2720},{"style":105},[2721],{"type":31,"value":82},{"type":26,"tag":98,"props":2723,"children":2724},{"style":135},[2725],{"type":31,"value":401},{"type":26,"tag":98,"props":2727,"children":2728},{"style":111},[2729],{"type":31,"value":143},{"type":26,"tag":98,"props":2731,"children":2732},{"style":135},[2733],{"type":31,"value":2617},{"type":26,"tag":98,"props":2735,"children":2736},{"style":105},[2737],{"type":31,"value":82},{"type":26,"tag":98,"props":2739,"children":2740},{"style":135},[2741],{"type":31,"value":2742},"customer_id\n",{"type":26,"tag":98,"props":2744,"children":2745},{"class":100,"line":368},[2746,2751,2756,2760,2765,2769,2774,2778,2782,2786],{"type":26,"tag":98,"props":2747,"children":2748},{"style":111},[2749],{"type":31,"value":2750},"left join",{"type":26,"tag":98,"props":2752,"children":2753},{"style":105},[2754],{"type":31,"value":2755}," order_item i ",{"type":26,"tag":98,"props":2757,"children":2758},{"style":111},[2759],{"type":31,"value":2712},{"type":26,"tag":98,"props":2761,"children":2762},{"style":135},[2763],{"type":31,"value":2764}," i",{"type":26,"tag":98,"props":2766,"children":2767},{"style":105},[2768],{"type":31,"value":82},{"type":26,"tag":98,"props":2770,"children":2771},{"style":135},[2772],{"type":31,"value":2773},"order_id",{"type":26,"tag":98,"props":2775,"children":2776},{"style":111},[2777],{"type":31,"value":143},{"type":26,"tag":98,"props":2779,"children":2780},{"style":135},[2781],{"type":31,"value":2617},{"type":26,"tag":98,"props":2783,"children":2784},{"style":105},[2785],{"type":31,"value":82},{"type":26,"tag":98,"props":2787,"children":2788},{"style":135},[2789],{"type":31,"value":2790},"id\n",{"type":26,"tag":98,"props":2792,"children":2794},{"class":100,"line":2793},14,[2795,2799,2803,2807,2811,2816,2821,2826,2830,2835,2839,2844],{"type":26,"tag":98,"props":2796,"children":2797},{"style":111},[2798],{"type":31,"value":925},{"type":26,"tag":98,"props":2800,"children":2801},{"style":135},[2802],{"type":31,"value":2617},{"type":26,"tag":98,"props":2804,"children":2805},{"style":105},[2806],{"type":31,"value":82},{"type":26,"tag":98,"props":2808,"children":2809},{"style":135},[2810],{"type":31,"value":401},{"type":26,"tag":98,"props":2812,"children":2813},{"style":111},[2814],{"type":31,"value":2815}," in",{"type":26,"tag":98,"props":2817,"children":2818},{"style":105},[2819],{"type":31,"value":2820}," (",{"type":26,"tag":98,"props":2822,"children":2823},{"style":135},[2824],{"type":31,"value":2825},"101",{"type":26,"tag":98,"props":2827,"children":2828},{"style":105},[2829],{"type":31,"value":335},{"type":26,"tag":98,"props":2831,"children":2832},{"style":135},[2833],{"type":31,"value":2834},"102",{"type":26,"tag":98,"props":2836,"children":2837},{"style":105},[2838],{"type":31,"value":335},{"type":26,"tag":98,"props":2840,"children":2841},{"style":135},[2842],{"type":31,"value":2843},"103",{"type":26,"tag":98,"props":2845,"children":2846},{"style":105},[2847],{"type":31,"value":154},{"type":26,"tag":98,"props":2849,"children":2851},{"class":100,"line":2850},15,[2852,2856,2860,2864,2869,2874,2878,2883,2887,2891,2895,2899,2903,2907,2911],{"type":26,"tag":98,"props":2853,"children":2854},{"style":111},[2855],{"type":31,"value":947},{"type":26,"tag":98,"props":2857,"children":2858},{"style":135},[2859],{"type":31,"value":2617},{"type":26,"tag":98,"props":2861,"children":2862},{"style":105},[2863],{"type":31,"value":82},{"type":26,"tag":98,"props":2865,"children":2866},{"style":135},[2867],{"type":31,"value":2868},"created_at",{"type":26,"tag":98,"props":2870,"children":2871},{"style":111},[2872],{"type":31,"value":2873}," desc",{"type":26,"tag":98,"props":2875,"children":2876},{"style":105},[2877],{"type":31,"value":335},{"type":26,"tag":98,"props":2879,"children":2880},{"style":135},[2881],{"type":31,"value":2882},"o",{"type":26,"tag":98,"props":2884,"children":2885},{"style":105},[2886],{"type":31,"value":82},{"type":26,"tag":98,"props":2888,"children":2889},{"style":135},[2890],{"type":31,"value":401},{"type":26,"tag":98,"props":2892,"children":2893},{"style":111},[2894],{"type":31,"value":2873},{"type":26,"tag":98,"props":2896,"children":2897},{"style":105},[2898],{"type":31,"value":335},{"type":26,"tag":98,"props":2900,"children":2901},{"style":135},[2902],{"type":31,"value":2651},{"type":26,"tag":98,"props":2904,"children":2905},{"style":105},[2906],{"type":31,"value":82},{"type":26,"tag":98,"props":2908,"children":2909},{"style":135},[2910],{"type":31,"value":401},{"type":26,"tag":98,"props":2912,"children":2913},{"style":105},[2914],{"type":31,"value":995},{"type":26,"tag":27,"props":2916,"children":2917},{},[2918],{"type":31,"value":2919},"Group the detail rows into response DTOs. Skip the second query for an empty page, preserve the first query's order, and carry tenant and authorization restrictions through both stages. If the client needs a total, budget for a separate count query.",{"type":26,"tag":27,"props":2921,"children":2922},{},[2923,2925,2932],{"type":31,"value":2924},"Consider consistency too: two statements at PostgreSQL's default Read Committed isolation can observe different committed data, even in one transaction. Decide whether that is acceptable for the search or whether a consistent snapshot is required. PostgreSQL documents these ",{"type":26,"tag":1476,"props":2926,"children":2929},{"href":2927,"rel":2928},"https://www.postgresql.org/docs/current/transaction-iso.html",[1480],[2930],{"type":31,"value":2931},"transaction isolation semantics",{"type":31,"value":82},{"type":26,"tag":27,"props":2934,"children":2935},{},[2936],{"type":31,"value":2937},"Paging parents does not bound the number of children within each parent. If a search needs only an item count or total, use an aggregate rather than loading every item. If it needs an enormous nested collection, that collection may need its own pagination.",{"type":26,"tag":60,"props":2939,"children":2941},{"id":2940},"verify-that-the-fix-survives-realistic-conditions",[2942],{"type":31,"value":2943},"Verify that the fix survives realistic conditions",{"type":26,"tag":27,"props":2945,"children":2946},{},[2947],{"type":31,"value":2948},"Repeat the original searches with the same dataset, page sizes, and concurrency. Check response correctness and ordering as carefully as latency. Record queries per request, rows fetched, connection-pool waits, and p50/p95/p99 response times alongside the average. A mean alone can hide the requests that hurt users most.",{"type":26,"tag":27,"props":2950,"children":2951},{},[2952],{"type":31,"value":2953},"Use both cold and warm application-cache conditions. A cache may hide repeated loads without removing the underlying pattern. Likewise, adding an index can improve each lookup without eliminating unnecessary round trips. Increasing the pool or parallelizing the lookups can move pressure onto PostgreSQL while leaving the cause intact.",{"type":26,"tag":27,"props":2955,"children":2956},{},[2957],{"type":31,"value":2958},"Add a focused integration check around the complete response mapping. For the scalar projection above, fetching 5 or 50 summaries should remain one select, assuming no unrelated work is included. For a batched design, assert the intended bounded growth instead of an arbitrary universal limit.",{"type":26,"tag":27,"props":2960,"children":2961},{},[2962],{"type":31,"value":2963},"Prepare and flush fixtures before measuring, start with a fresh persistence context, and control the second-level cache. Include distinct related entities. Count statements for the operation under test; global ORM statistics can mix in other requests or parallel tests. The regression to catch is a new mapper field quietly turning a bounded query into one lookup per result.",{"type":26,"tag":60,"props":2965,"children":2967},{"id":2966},"make-performance-part-of-everyday-ownership",[2968],{"type":31,"value":2969},"Make performance part of everyday ownership",{"type":26,"tag":27,"props":2971,"children":2972},{},[2973],{"type":31,"value":2974},"A useful next step is small: choose a search endpoint you maintain, call it with a representative page, and read the SQL it produces. Can you explain every query? Does the amount of database work match what the response needs?",{"type":26,"tag":27,"props":2976,"children":2977},{},[2978],{"type":31,"value":2979},"Knowing N+1 by name is a starting point. Recognizing it in familiar code, proving the cause, and checking the fix are skills we can practise and share. Our response times represent our work as a team; paying attention to them is one practical way to take care of that work and the people using it.",{"type":26,"tag":60,"props":2981,"children":2983},{"id":2982},"further-reading",[2984],{"type":31,"value":2985},"Further reading",{"type":26,"tag":2987,"props":2988,"children":2989},"ul",{},[2990,3002,3012,3022],{"type":26,"tag":2991,"props":2992,"children":2993},"li",{},[2994,3001],{"type":26,"tag":1476,"props":2995,"children":2998},{"href":2996,"rel":2997},"https://docs.hibernate.org/orm/6.6/userguide/html_single/#fetching",[1480],[2999],{"type":31,"value":3000},"Hibernate: fetching strategies",{"type":31,"value":82},{"type":26,"tag":2991,"props":3003,"children":3004},{},[3005,3011],{"type":26,"tag":1476,"props":3006,"children":3008},{"href":1971,"rel":3007},[1480],[3009],{"type":31,"value":3010},"Spring Data JPA: projections",{"type":31,"value":82},{"type":26,"tag":2991,"props":3013,"children":3014},{},[3015,3021],{"type":26,"tag":1476,"props":3016,"children":3018},{"href":1657,"rel":3017},[1480],[3019],{"type":31,"value":3020},"PostgreSQL: pg_stat_statements",{"type":31,"value":82},{"type":26,"tag":2991,"props":3023,"children":3024},{},[3025,3031],{"type":26,"tag":1476,"props":3026,"children":3028},{"href":1743,"rel":3027},[1480],[3029],{"type":31,"value":3030},"PostgreSQL: using EXPLAIN",{"type":31,"value":82},{"type":26,"tag":3033,"props":3034,"children":3035},"style",{},[3036],{"type":31,"value":3037},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":8,"searchDepth":117,"depth":117,"links":3039},[3040,3041,3042,3047,3052,3053,3054,3055],{"id":62,"depth":117,"text":65},{"id":1150,"depth":117,"text":1153},{"id":1181,"depth":117,"text":1184,"children":3043},[3044,3045,3046],{"id":1188,"depth":157,"text":1191},{"id":1320,"depth":157,"text":1323},{"id":1497,"depth":157,"text":1500},{"id":1752,"depth":117,"text":1755,"children":3048},[3049,3050,3051],{"id":1763,"depth":157,"text":1766},{"id":1980,"depth":157,"text":1983},{"id":2245,"depth":157,"text":2248},{"id":2355,"depth":117,"text":2358},{"id":2940,"depth":117,"text":2943},{"id":2966,"depth":117,"text":2969},{"id":2982,"depth":117,"text":2985},"markdown","content:posts:n-plus-one-queries.md","content","posts/n-plus-one-queries.md","posts/n-plus-one-queries","md",1791120244651]