{"id":11689,"date":"2022-09-20T18:29:44","date_gmt":"2022-09-20T18:29:44","guid":{"rendered":"https:\/\/predictly.se\/jpa-searches-with-spring-data-example\/"},"modified":"2024-05-08T11:46:43","modified_gmt":"2024-05-08T11:46:43","slug":"jpa-searches-with-spring-data-example","status":"publish","type":"post","link":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/","title":{"rendered":"JPA searches with Spring Data Example"},"content":{"rendered":"<p>A very common use case is to easily find all or a subset of records from a database, often in combination with paging so that a user can iterate through the result. This has lead us to investigate the possibility of searching databases using Spring Data JPA.<\/p>\n<p>In this article, I will use the <a href=\"https:\/\/dev.mysql.com\/doc\/employee\/en\/employees-introduction.html\" target=\"_blank\" rel=\"noopener\">MySQL Employees<\/a> database via the docker container <a href=\"https:\/\/hub.docker.com\/r\/genschsa\/mysql-employees\" target=\"_blank\" rel=\"noopener\">genschsa\/mysql-employees<\/a>, see my previous <a href=\"https:\/\/www.predictly.se\/spring-data-jpa-batchjobb-med-streams\/\" target=\"_blank\" rel=\"noopener\">post<\/a> for relevant instructions about this container. Entity model for Employee is given below.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n@Entity\n@Table(name = &quot;employees&quot;)\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Employee {\n    \n  @Id\n  @Column(name = &quot;emp_no&quot;)\n  private int employeeId;\n\n  @Column(name = &quot;birth_date&quot;)\n  private LocalDate birthDate;\n    \n  @Column(name = &quot;first_name&quot;)\n  private String firstName;\n    \n  @Column(name = &quot;last_name&quot;)\n  private String lastName;\n    \n  @Column(name = &quot;gender&quot;, columnDefinition = &quot;ENUM(&#039;M&#039;, &#039;F&#039;)&quot;, nullable = false)\n  @Enumerated(EnumType.STRING)\n  Private Gender Gender;\n\n  @Column(name = &quot;hire_date&quot;)\n  private LocalDate hireDate;\n\n  @OneToMany(mappedBy = &quot;employeeId&quot;, cascade = CascadeType.ALL, orphanRemoval = true)\n  private Set&amp;lt;Salary&amp;gt; salaries;\n\n  @OneToMany(mappedBy = &quot;employeeId&quot;, cascade = CascadeType.ALL, orphanRemoval = true)\n  private Set&amp;lt;Title&amp;gt; titles;\n}\n<\/pre>\n<h6>Searching using Spring Data JPA queries<\/h6>\n<p>One initial approach is of course to use the built in Spring Data JPA support for searching based on declaring methods in your repository. Let&#8217;s say we want to find all employees based on their birthDate we can easily do that by declaring a query in our JPA repository interface. But what if we want to give our users the ability to search for first and last name as well? And then what if any of the parameters are passed as <i>null<\/i>? This quickly grows out of hand and you end up with a repository looking like this<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\npackage se.predictly.support.employee.persistence;\n\nimport java.time.LocalDate;\nimport java.util.List;\n\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\nimport se.predictly.support.employee.persistence.model.Employee;\n\n@Repository\npublic interface EmployeeRepository extends JpaRepository&amp;lt;Employee, Integer&amp;gt; {\n\n  List&amp;lt;Employee&amp;gt; findByBirthDate(LocalDate birthDate, Pageable pageable);\n  List&amp;lt;Employee&amp;gt; findByFirstName(String firstName, Pageable pageable);\n  List&amp;lt;Employee&amp;gt; findByLastName(String lastName, Pageable pageable);\n\n  List&amp;lt;Employee&amp;gt; findByBirthDateAndFirstName(LocalDate birthDate, String firstName, Pageable pageable);\n  List&amp;lt;Employee&amp;gt; findByBirthDateOrLastName(LocalDate birthDate, String firstName, Pageable pageable);\n  List&amp;lt;Employee&amp;gt; findByFirstNameOrLastName(String firstName, String lastName, Pageable pageable);\n\n  List&amp;lt;Employee&amp;gt; findByBirthDateOrFirstNameOrLastName(LocalDate birthDate, String firstName, Pageable pageable);\n}\n<\/pre>\n<p>And then an even more problematic service class<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n@Autowired\nprivate EmployeeRepository employeeRepo;\n\n@Transactional(readOnly = true)\npublic List&amp;lt;EmployeeVO&amp;gt; listEmployees(int page, int pageSize, String sortField, Sort.Direction sortDirection, LocalDate birthDate, String firstName, String lastName) {\n  Pageable pageable = pageable(page, pageSize, sortField, sortDirection);\n\n  List&amp;lt;Employee&amp;gt; employees = null;\n  if (birthDate != null) {\n  if (StringUtils.hasText(firstName)) {\n  if (StringUtils.hasText(lastName)) {\n  \/\/ All three sets\n  employees = employeeRepo.findByBirthDateOrFirstNameOrLastName(birthDate, firstName, lastName, pageable);\n  } else {\n  employees = employeeRepo.findByBirthDateOrFirstName(birthDate, firstName, pageable);\n  }\n  } else if (StringUtils.hasText(lastName)) {\n  \/\/ Oh dear, here we go...\n  employees = employeeRepo.findByBirthDateOrLastName(birthDate, lastName, pageable);\n  }\n  \/\/ There are more scenarios to cover here...\n  }\n\n  return employees.stream()\n  .map(EmployeeMapper::map)\n  .collect(Collectors.toList());\n}\n<\/pre>\n<p>Obviously, you see where this is going. Any new field added to the search creates an unmaintainable increase in combinations of parameters.<\/p>\n<h6>Searching done easy using Example<\/h6>\n<p>Instead of explicitly covering each combination of parameter that can be passed by the client, we can instead rely on the built in support for querying using an example entity. This then takes care of the problem of creating the correct SQL based on our search criterias (exact, like, etc.) and what values has been set.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate static final ExampleMatcher SEARCH_CONDITIONS_MATCH_ANY = ExampleMatcher\n  .matchingAny()\n  .withMatcher(&quot;birthDate&quot;, ExampleMatcher.GenericPropertyMatchers.exact())\n  .withMatcher(&quot;firstName&quot;, ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())\n  .withMatcher(&quot;lastName&quot;, ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())\n  .withIgnorePaths(&quot;employeeId&quot;, &quot;gender&quot;, &quot;hireDate&quot;, &quot;salaries&quot;, &quot;titles&quot;);\n\nprivate static final ExampleMatcher SEARCH_CONDITIONS_MATCH_ALL = ExampleMatcher\n  .matching()\n  .withMatcher(&quot;birthDate&quot;, ExampleMatcher.GenericPropertyMatchers.exact())\n  .withMatcher(&quot;firstName&quot;, ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())\n  .withMatcher(&quot;lastName&quot;, ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())\n  .withIgnorePaths(&quot;employeeId&quot;, &quot;gender&quot;, &quot;hireDate&quot;, &quot;salaries&quot;, &quot;titles&quot;);\n\n@Autowired\nprivate EmployeeRepository employeeRepo;\n\n@Transactional(readOnly = true)\npublic List&amp;lt;EmployeeVO&amp;gt; listEmployeesUsingExample(int page, int pageSize, String sortField, Sort.Direction sortDirection, LocalDate birthDate, String firstName, String lastName, boolean matchAll) {\n  Pageable pageable = pageable(page, pageSize, sortField, sortDirection);\n\n  Employee employee = Employee.builder()\n  .birthDate(birthDate)\n  .firstName(firstName)\n  .lastName(lastName)\n  .build();\n  Example&amp;lt;Employee&amp;gt; example = Example.of(employee, matchAll ? SEARCH_CONDITIONS_MATCH_ALL : SEARCH_CONDITIONS_MATCH_ANY);\n\n  Page&amp;lt;Employee&amp;gt; employees = employeeRepo.findAll(example, pageable);\n\n  return employees.stream()\n  .map(EmployeeMapper::map)\n  .collect(Collectors.toList());\n}\n\nprivate Pageable pageable(int page, int pageSize, String sortField, Direction sortDirection) {\n  return PageRequest.of(page, pageSize, sortDirection, sortField);\n}\n<\/pre>\n<h6>Search Criteria<\/h6>\n<p>First we need to define what criterias to use when searching for our records, this is done by creating an <b>ExampleMatcher<\/b>. This is used to instruct Spring Data JPA how to perform the search, <i>matchingAll<\/i> or <i>matchingAny<\/i>, what fields to include in the search and which to exclude, and finally how each field should be handled in the actual search, <i>exact<\/i> or <i>contains<\/i> etc.<\/p>\n<p>When declaring our search criteria, we must take care to <b>explicitly cover each field<\/b> of the entity as either an included or an excluded field. Otherwise it will be included in the search and not produce the result we expect.<\/p>\n<h6>Creating an Example<\/h6>\n<p>The next step is to setup our Example, i.e. an example that records in the database must match to be included in the result. Above we have have specified three different attributes that should be included in the search and we set them on line 22-26.<\/p>\n<p>Spring Data JPA then supports converting the example entity to proper SQL given the search criteria specified and it even knows how to handle null values, removing a lot of problems from us when developing our search solution.<\/p>\n<p>At this point, care should be taken to make sure that the database isn&#8217;t overloaded, perhaps utilizing and index and enforcing that attribute as a required parameter.<\/p>\n<h6>Performing searches<\/h6>\n<p>We are now ready to start querying our database for records and then we can inspect the generated SQL to see how each combination is handled.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Searching using birthDate, firstName, lastName and matching all\nselect\n  employee0_.*  \nfrom\n  employees employee0_  \nwhere  \n  lower(employee0_.last_name) like ? escape ? and -- and condition since we have specified that it must match all\n  employee0_.birth_date=? and\n  lower(employee0_.first_name) like ? escape ?\norder by\n  employee0_.emp_no desc limit ?\n\n-- Searching using birthDate and matching all\nselect\n  employee0_.*  \nfrom\n  employees employee0_  \nwhere  \n  employee0_.birth_date=?  \norder by\n  employee0_.emp_no desc limit ?\n\n-- Searching using no criteria and matching all\nselect\n  employee0_.*  \nfrom\n  employees employee0_  \nwhere  \n  ?=1 -- 1=1 statement to include where clause\norder by\n  employee0_.emp_no desc limit ?\n\n-- Searching using birthDate, firstName, lastName and matching any\nselect\n  employee0_.*  \nfrom\n  employees employee0_  \nwhere  \n  lower(employee0_.last_name) like ? escape ? or -- or condition since we have specified that it can match any\n  employee0_.birth_date=? gold\n  lower(employee0_.first_name) like ? escape ?\norder by\n  employee0_.emp_no desc limit ?\n<\/pre>\n<p>As can be seen, Spring Data JPA automatically adapts to what fields are set and it creates conditions based on the ExampleMatcher criteria we have defined.<\/p>\n<p>Designing and performing database searches using <i>Example<\/i> and <i>ExampleMatcher<\/i> makes it a lot easier to provide rich search experiences based on JPA and traditional SQL databases and takes care of a lot of the pains for us.<\/p>\n<h6>Limitations<\/h6>\n<p>Searching using Example is great but it does come with some limitations. For starters, it does not support searching in sub collections using JOIN statements. It is also somewhat limited in property matchers available, but this can of course be extended freely.<\/p>\n<p>For more advanced searches than just querying one single table, we have to turn to <i>Specifications<\/i>. <a href=\"https:\/\/www.predictly.se\/komplexa-jpa-queries-med-specifications\/\">More on that in a later post!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to easily search for complex JPA entities in one simple method<\/p>\n","protected":false},"author":6,"featured_media":11383,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"cybocfi_hide_featured_image":"","footnotes":""},"categories":[120,119,117,118],"tags":[],"class_list":["post-11689","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application","category-java-en","category-jpa-en","category-spring-en"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>JPA searches with Spring Data Example &#8211; Predictly<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"JPA searches with Spring Data Example &#8211; Predictly\" \/>\n<meta property=\"og:description\" content=\"How to easily search for complex JPA entities in one simple method\" \/>\n<meta property=\"og:url\" content=\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\" \/>\n<meta property=\"og:site_name\" content=\"Predictly\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/predictly.se\" \/>\n<meta property=\"article:published_time\" content=\"2022-09-20T18:29:44+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-08T11:46:43+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1426\" \/>\n\t<meta property=\"og:image:height\" content=\"882\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Patrik H\u00f6rlin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@predictly_se\" \/>\n<meta name=\"twitter:site\" content=\"@predictly_se\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Patrik H\u00f6rlin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\"},\"author\":{\"name\":\"Patrik H\u00f6rlin\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3\"},\"headline\":\"JPA searches with Spring Data Example\",\"datePublished\":\"2022-09-20T18:29:44+00:00\",\"dateModified\":\"2024-05-08T11:46:43+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\"},\"wordCount\":1312,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/predictly.se\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg\",\"articleSection\":[\"Application\",\"Java\",\"JPA\",\"Spring\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\",\"url\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\",\"name\":\"JPA searches with Spring Data Example &#8211; Predictly\",\"isPartOf\":{\"@id\":\"https:\/\/predictly.se\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg\",\"datePublished\":\"2022-09-20T18:29:44+00:00\",\"dateModified\":\"2024-05-08T11:46:43+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage\",\"url\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg\",\"contentUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg\",\"width\":1426,\"height\":882},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/predictly.se\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Okategoriserad\",\"item\":\"https:\/\/predictly.se\/en\/insikter\/okategoriserad\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"JPA searches with Spring Data Example\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/predictly.se\/en\/#website\",\"url\":\"https:\/\/predictly.se\/en\/\",\"name\":\"Predictly\",\"description\":\"Professional IT services\",\"publisher\":{\"@id\":\"https:\/\/predictly.se\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/predictly.se\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/predictly.se\/en\/#organization\",\"name\":\"Predictly\",\"url\":\"https:\/\/predictly.se\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg\",\"contentUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg\",\"width\":532,\"height\":96,\"caption\":\"Predictly\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/predictly.se\",\"https:\/\/x.com\/predictly_se\",\"https:\/\/www.linkedin.com\/company\/predictly\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3\",\"name\":\"Patrik H\u00f6rlin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g\",\"caption\":\"Patrik H\u00f6rlin\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"JPA searches with Spring Data Example &#8211; Predictly","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/","og_locale":"en_US","og_type":"article","og_title":"JPA searches with Spring Data Example &#8211; Predictly","og_description":"How to easily search for complex JPA entities in one simple method","og_url":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/","og_site_name":"Predictly","article_publisher":"https:\/\/www.facebook.com\/predictly.se","article_published_time":"2022-09-20T18:29:44+00:00","article_modified_time":"2024-05-08T11:46:43+00:00","og_image":[{"width":1426,"height":882,"url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg","type":"image\/jpeg"}],"author":"Patrik H\u00f6rlin","twitter_card":"summary_large_image","twitter_creator":"@predictly_se","twitter_site":"@predictly_se","twitter_misc":{"Written by":"Patrik H\u00f6rlin","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#article","isPartOf":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/"},"author":{"name":"Patrik H\u00f6rlin","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3"},"headline":"JPA searches with Spring Data Example","datePublished":"2022-09-20T18:29:44+00:00","dateModified":"2024-05-08T11:46:43+00:00","mainEntityOfPage":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/"},"wordCount":1312,"commentCount":0,"publisher":{"@id":"https:\/\/predictly.se\/en\/#organization"},"image":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage"},"thumbnailUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg","articleSection":["Application","Java","JPA","Spring"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/","url":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/","name":"JPA searches with Spring Data Example &#8211; Predictly","isPartOf":{"@id":"https:\/\/predictly.se\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage"},"image":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage"},"thumbnailUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg","datePublished":"2022-09-20T18:29:44+00:00","dateModified":"2024-05-08T11:46:43+00:00","breadcrumb":{"@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#primaryimage","url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg","contentUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/blog-Image3a.jpg","width":1426,"height":882},{"@type":"BreadcrumbList","@id":"https:\/\/predictly.se\/en\/jpa-searches-with-spring-data-example\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/predictly.se\/en\/"},{"@type":"ListItem","position":2,"name":"Okategoriserad","item":"https:\/\/predictly.se\/en\/insikter\/okategoriserad\/"},{"@type":"ListItem","position":3,"name":"JPA searches with Spring Data Example"}]},{"@type":"WebSite","@id":"https:\/\/predictly.se\/en\/#website","url":"https:\/\/predictly.se\/en\/","name":"Predictly","description":"Professional IT services","publisher":{"@id":"https:\/\/predictly.se\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/predictly.se\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/predictly.se\/en\/#organization","name":"Predictly","url":"https:\/\/predictly.se\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/","url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg","contentUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg","width":532,"height":96,"caption":"Predictly"},"image":{"@id":"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/predictly.se","https:\/\/x.com\/predictly_se","https:\/\/www.linkedin.com\/company\/predictly\/"]},{"@type":"Person","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3","name":"Patrik H\u00f6rlin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g","caption":"Patrik H\u00f6rlin"}}]}},"_links":{"self":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11689","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/comments?post=11689"}],"version-history":[{"count":1,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11689\/revisions"}],"predecessor-version":[{"id":11701,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11689\/revisions\/11701"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/media\/11383"}],"wp:attachment":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/media?parent=11689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/categories?post=11689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/tags?post=11689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}