JPA: How to get entity based on field value other than ID?

前端 未结 15 924
时光说笑
时光说笑 2020-12-04 21:08

In JPA (Hibernate), when we automatically generate the ID field, it is assumed that the user has no knowledge about this key. So, when obtaining the entity, user would query

相关标签:
15条回答
  • 2020-12-04 21:42

    Using CrudRepository and JPA query works for me:

    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.CrudRepository;
    import org.springframework.data.repository.query.Param;
    
    public interface TokenCrudRepository extends CrudRepository<Token, Integer> {
    
     /**
     * Finds a token by using the user as a search criteria.
     * @param user
     * @return  A token element matching with the given user.
     */
        @Query("SELECT t FROM Token t WHERE LOWER(t.user) = LOWER(:user)")
        public Token find(@Param("user") String user);
    
    }
    

    and you invoke the find custom method like this:

    public void destroyCurrentToken(String user){
        AbstractApplicationContext context = getContext();
    
        repository = context.getBean(TokenCrudRepository.class);
    
        Token token = ((TokenCrudRepository) repository).find(user);
    
        int idToken = token.getId();
    
        repository.delete(idToken);
    
        context.close();
    }
    
    0 讨论(0)
  • 2020-12-04 21:43

    It is not a "problem" as you stated it.

    Hibernate has the built-in find(), but you have to build your own query in order to get a particular object. I recommend using Hibernate's Criteria :

    Criteria criteria = session.createCriteria(YourClass.class);
    YourObject yourObject = criteria.add(Restrictions.eq("yourField", yourFieldValue))
                                 .uniqueResult();
    

    This will create a criteria on your current class, adding the restriction that the column "yourField" is equal to the value yourFieldValue. uniqueResult() tells it to bring a unique result. If more objects match, you should retrive a list.

    List<YourObject> list = criteria.add(Restrictions.eq("yourField", yourFieldValue)).list();
    

    If you have any further questions, please feel free to ask. Hope this helps.

    0 讨论(0)
  • 2020-12-04 21:45

    I've written a library that helps do precisely this. It allows search by object simply by initializing only the fields you want to filter by: https://github.com/kg6zvp/GenericEntityEJB

    0 讨论(0)
  • 2020-12-04 21:46

    All the answers require you to write some sort of SQL/HQL/whatever. Why? You don't have to - just use CriteriaBuilder:

    Person.java:

    @Entity
    class Person  {
      @Id @GeneratedValue
      private int id;
    
      @Column(name = "name")
      private String name;
      @Column(name = "age")
      private int age;
      ...
    }
    

    Dao.java:

    public class Dao  {
      public static Person getPersonByName(String name)  {
            SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
            Session session = sessionFactory.openSession();
            session.beginTransaction();
    
            CriteriaBuilder cb = session.getCriteriaBuilder();
    
            CriteriaQuery<Person> cr = cb.createQuery(Person.class);
            Root<Person> root = cr.from(Person.class);
            cr.select(root).where(cb.equal(root.get("name"), name));  //here you pass a class field, not a table column (in this example they are called the same)
    
            Query<Person> query = session.createQuery(cr);
            query.setMaxResults(1);
            List<Person> result = query.getResultList();
            session.close();
    
            return result.get(0);
      }
    }
    

    example of use:

    public static void main(String[] args)  {
      Person person = Dao.getPersonByName("John");
      System.out.println(person.getAge());  //John's age
    }
    
    0 讨论(0)
  • 2020-12-04 21:47

    if you have repository for entity Foo and need to select all entries with exact string value boo (also works for other primitive types or entity types). Put this into your repository interface:

    List<Foo> findByBoo(String boo);
    

    if you need to order results:

    List<Foo> findByBooOrderById(String boo);
    

    See more at reference.

    0 讨论(0)
  • 2020-12-04 21:49

    Edit: Just realized that @Chinmoy was getting at basically the same thing, but I think I may have done a better job ELI5 :)

    If you're using a flavor of Spring Data to help persist / fetch things from whatever kind of Repository you've defined, you can probably have your JPA provider do this for you via some clever tricks with method names in your Repository interface class. Allow me to explain.

    (As a disclaimer, I just a few moments ago did/still am figuring this out for myself.)

    For example, if I am storing Tokens in my database, I might have an entity class that looks like this:

    @Data // << Project Lombok convenience annotation
    @Entity
    public class Token {
        @Id
        @Column(name = "TOKEN_ID")
        private String tokenId;
    
        @Column(name = "TOKEN")
        private String token;
    
        @Column(name = "EXPIRATION")
        private String expiration;
    
        @Column(name = "SCOPE")
        private String scope;
    }
    

    And I probably have a CrudRepository<K,V> interface defined like this, to give me simple CRUD operations on that Repository for free.

    @Repository
    // CrudRepository<{Entity Type}, {Entity Primary Key Type}>
    public interface TokenRepository extends CrudRepository<Token, String> { }
    

    And when I'm looking up one of these tokens, my purpose might be checking the expiration or scope, for example. In either of those cases, I probably don't have the tokenId handy, but rather just the value of a token field itself that I want to look up.

    To do that, you can add an additional method to your TokenRepository interface in a clever way to tell your JPA provider that the value you're passing in to the method is not the tokenId, but the value of another field within the Entity class, and it should take that into account when it is generating the actual SQL that it will run against your database.

    @Repository
    // CrudRepository<{Entity Type}, {Entity Primary Key Type}>
    public interface TokenRepository extends CrudRepository<Token, String> { 
        List<Token> findByToken(String token);
    }
    

    I read about this on the Spring Data R2DBC docs page, and it seems to be working so far within a SpringBoot 2.x app storing in an embedded H2 database.

    0 讨论(0)
提交回复
热议问题