簡體   English   中英

JPA:如何根據ID以外的字段值獲取實體?

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

在JPA(Hibernate)中,當我們自動生成ID字段時,假設用戶不知道這個key。 因此,在獲取實體時,用戶會根據ID以外的某些字段進行查詢。 在那種情況下我們如何獲得實體(因為不能使用em.find() )。

我知道我們可以使用查詢並稍后過濾結果。 但是,有沒有更直接的方法(因為據我了解這是一個非常普遍的問題)。

這不是您所說的“問題”。

Hibernate 具有內置的find() ,但您必須構建自己的查詢才能獲取特定對象。 我建議使用HibernateCriteria

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

這將在您當前的類上創建一個criteria ,添加列“yourField”等於值yourFieldValue uniqueResult()告訴它帶來一個獨特的結果。 如果更多的對象匹配,你應該檢索一個列表。

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

如果您有任何其他問題,請隨時提問。 希望這可以幫助。

如果您有實體Foo存儲庫並且需要選擇具有精確字符串值boo所有條目(也適用於其他原始類型或實體類型)。 將其放入您的存儲庫界面:

List<Foo> findByBoo(String boo);

如果您需要訂購結果:

List<Foo> findByBooOrderById(String boo);

參考中查看更多信息

基本上,您應該添加一個特定的唯一字段。 我通常使用xxxUri字段。

class User {

    @Id
    // automatically generated
    private Long id;

    // globally unique id
    @Column(name = "SCN", nullable = false, unique = true)
    private String scn;
}

你的商業方法會這樣做。

public User findUserByScn(@NotNull final String scn) {
    CriteriaBuilder builder = manager.getCriteriaBuilder();
    CriteriaQuery<User> criteria = builder.createQuery(User.class);
    Root<User> from = criteria.from(User.class);
    criteria.select(from);
    criteria.where(builder.equal(from.get(User_.scn), scn));
    TypedQuery<User> typed = manager.createQuery(criteria);
    try {
        return typed.getSingleResult();
    } catch (final NoResultException nre) {
        return null;
    }
}

最佳實踐是使用 @NaturalId 注釋。 在某些情況下它可以用作業務鍵它太復雜了,因此在現實世界中某些字段用作標識符。 例如,我有一個以用戶 ID 作為主鍵的用戶類,電子郵件也是唯一的字段。 所以我們可以使用電子郵件作為我們的自然 ID

@Entity
@Table(name="user")
public class User {
  @Id
  @Column(name="id")
  private int id;

  @NaturalId
  @Column(name="email")
  private String email;

  @Column(name="name")
  private String name;
}

要獲得我們的記錄,只需簡單地使用“session.byNaturalId()”

Session session = sessionFactory.getCurrentSession();
User user = session.byNaturalId(User.class)
                   .using("email","huchenhai@qq.com")
                   .load()

這個解決方案來自Beginning Hibernate書:

     Query<User> query = session.createQuery("from User u where u.scn=:scn", User.class);
     query.setParameter("scn", scn);
     User user = query.uniqueResult();    

編寫一個這樣的自定義方法:

public Object findByYourField(Class entityClass, String yourFieldValue)
{
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery(entityClass);
    Root<Object> root = criteriaQuery.from(entityClass);
    criteriaQuery.select(root);

    ParameterExpression<String> params = criteriaBuilder.parameter(String.class);
    criteriaQuery.where(criteriaBuilder.equal(root.get("yourField"), params));

    TypedQuery<Object> query = entityManager.createQuery(criteriaQuery);
    query.setParameter(params, yourFieldValue);

    List<Object> queryResult = query.getResultList();

    Object returnObject = null;

    if (CollectionUtils.isNotEmpty(queryResult)) {
        returnObject = queryResult.get(0);
    }

    return returnObject;
}

我解決了一個類似的問題,我想通過 isbnCode 而不是你的 id(主鍵)來查找一本書。

@Entity
public class Book implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    private String isbnCode;

...

在存儲庫中,該方法是像提到的@kamalveer singh 一樣創建的。 請注意,方法名稱是 findBy+fieldName(在我的例子中:findByisbnCode):

@Repository
public interface BookRepository extends JpaRepository<Book, Integer> {

    Book findByisbnCode(String isbnCode);
}

然后,在服務中實現方法:

@Service
public class BookService {

    @Autowired
    private BookRepository repo;
    
    
    public Book findByIsbnCode(String isbnCode) {
        Book obj = repo.findByisbnCode(isbnCode);
        return obj; 
    }
}

不,您不需要進行條件查詢,這將是樣板代碼,如果您在 Spring-boot 中工作,您只需做簡單的事情:在您的存儲庫中使用 findBy[exact field name] 聲明一個方法名稱。 示例 - 如果您的模型或文檔包含一個字符串字段myField並且您想通過它查找,那么您的方法名稱將是:

findBymyField(String myField);

看一下:

我寫了一個庫,可以幫助做到這一點。 它允許通過僅初始化您要過濾的字段來按對象進行搜索: https : //github.com/kg6zvp/GenericEntityEJB

在我的 Spring Boot 應用程序中,我解決了類似的問題:

@Autowired
private EntityManager entityManager;

public User findByEmail(String email) {
    User user = null;
    Query query = entityManager.createQuery("SELECT u FROM User u WHERE u.email=:email");
    query.setParameter("email", email);
    try {
        user = (User) query.getSingleResult();
    } catch (Exception e) {
        // Handle exception
    }
    return user;
}

參考 - 查詢方法的 Spring 文檔

我們可以通過在如下方法中傳遞 diff 參數來在 Spring Jpa 中添加方法:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

// Enabling static ORDER BY for a query 

List<Person> findByLastnameOrderByFirstnameAsc(String lastname);

編輯:剛剛意識到@Chinmoy 基本上是在做同樣的事情,但我想我可能做得更好 ELI5 :)

如果您使用 Spring Data 的一種風格來幫助從您定義的任何類型的Repository持久化/獲取事物,您可能可以讓您的 JPA 提供者通過一些巧妙的技巧為您執行此操作,並在您的Repository接口類中使用方法名稱。 請允許我解釋一下。

(作為免責聲明,我剛才做了/仍在為自己解決這個問題。)

例如,如果我在我的數據庫中存儲令牌,我可能有一個如下所示的實體類:

@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;
}

而且我可能有一個像這樣定義的CrudRepository<K,V>接口,以便免費為我提供對該存儲庫的簡單 CRUD 操作。

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

例如,當我查找這些令牌之一時,我的目的可能是檢查過期或范圍。 在這兩種情況中的任何一種情況下,我可能都沒有方便的tokenId ,而只是我想要查找的token字段本身的值。

為此,您可以巧妙地向TokenRepository接口添加一個額外的方法,以告訴您的 JPA 提供者您傳遞給該方法的值不是tokenId ,而是 Entity 類中另一個字段的值,它應該在生成將針對您的數據庫運行的實際 SQL 時考慮到這一點。

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

我在Spring Data R2DBC 文檔頁面上讀到了這個,到目前為止它似乎在存儲在嵌入式 H2 數據庫中的 SpringBoot 2.x 應用程序中工作。

所有的答案都要求您編寫某種 SQL/HQL/任何東西。 為什么? 您不必 - 只需使用CriteriaBuilder

人.java :

@Entity
class Person  {
  @Id @GeneratedValue
  private int id;

  @Column(name = "name")
  private String name;
  @Column(name = "age")
  private int age;
  ...
}

道.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);
  }
}

使用示例:

public static void main(String[] args)  {
  Person person = Dao.getPersonByName("John");
  System.out.println(person.getAge());  //John's age
}

這是非常基本的查詢:

實體:學生

@Entity
@Data
@NoArgsConstructor
public class Student{
@Id
@GeneratedValue(generator = "uuid2", strategy = GenerationType.IDENTITY)
@GenericGenerator(name = "uuid2", strategy = "uuid2")
private String id;

@Column(nullable = false)
@Version
@JsonIgnore
private Integer version;

private String studentId;

private String studentName;

private OffsetDateTime enrollDate;

}

存儲庫接口: StudentRepository

@Repository
public interface StudentRepository extends JpaRepository<Student, String> {

List<Student> findByStudentName(String studentName);

List<Student> findByStudentNameOrderByEnrollDateDesc(String studentName);

@Transactional
@Modifying
void deleteByStudentName(String studentName);
} 

注意:findByColumnName:按條件給出結果

List findByStudentName(String studentName) 內部轉換成query: select * from Student where name='studentName'

@Transactional @Modifying當您想從數據庫中刪除持久數據時很有用。

使用 CrudRepository 和 JPA 查詢對我有用:

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);

}

然后像這樣調用 find 自定義方法:

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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM