在JPA(Hibernate)中,当我们自动生成ID字段时,假设用户不知道该密钥。因此,当获得实体时,用户将基于除ID之外的某些字段进行查询。在这种情况下我们如何获得实体(因为em.find()
不能使用)。
我知道我们可以使用查询并稍后过滤结果。但是,是否有更直接的方式(因为这是我理解的一个非常常见的问题)。
答案 0 :(得分:31)
这不是你所说的“问题”。
Hibernate具有内置的find()
,但您必须构建自己的查询才能获取特定对象。我建议使用 Hibernate 的Criteria
:
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();
如果您有任何其他问题,请随时提出。希望这会有所帮助。
答案 1 :(得分:15)
如果您有实体Foo
的存储库,并且需要选择具有精确字符串值boo
的所有条目(也适用于其他基本类型或实体类型)。将它放入您的存储库界面:
List<Foo> findByBoo(String boo);
如果您需要订购结果:
List<Foo> findByBooOrderById(String boo);
请参阅reference。
答案 2 :(得分:7)
基本上,您应该添加特定的唯一字段。我通常使用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;
}
}
答案 3 :(得分:2)
编写一个这样的自定义方法:
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;
}
答案 4 :(得分:2)
最佳做法是使用@NaturalId注释。它可以用作某些情况下的业务密钥太复杂,因此有些字段用作现实世界中的标识符。 例如,我的用户类具有用户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;
}
要获取我们的记录,只需使用&#39; session.byNaturalId()&#39;
Session session = sessionFactory.getCurrentSession();
User user = session.byNaturalId(User.class)
.using("email","huchenhai@qq.com")
.load()
答案 5 :(得分:1)
此解决方案摘自《 Hibernate初学》一书:
sudo mvn -Dtest=PerfLatency#latencyTest test -X
答案 6 :(得分:1)
我解决了一个类似的问题,我想通过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;
}
}
答案 7 :(得分:0)
看看:
JPA query language
:The Java Persistence Query Language JPA Criteria API
:Using the Criteria API to Create Queries 答案 8 :(得分:0)
我已经写了一个有助于做到这一点的图书馆。它允许按对象搜索,只需初始化您要过滤的字段:https://github.com/kg6zvp/GenericEntityEJB
答案 9 :(得分:0)
在我的Spring启动应用程序中,我解决了类似的问题:
@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;
}
答案 10 :(得分:0)
引荐-Spring docs for query methods
我们可以通过在以下方法中传递diff params来在Spring Jpa中添加方法:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
答案 11 :(得分:0)
编辑:刚刚意识到@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 docs page上了解了这一点,到目前为止,它似乎在存储在嵌入式H2数据库中的SpringBoot 2.x应用程序中仍能正常工作。
答案 12 :(得分:0)
所有答案都要求您编写某种SQL / HQL /其他内容。为什么?您不必-只需使用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);
}
}
使用示例:
public static void main(String[] args) {
Person person = Dao.getPersonByName("John");
System.out.println(person.getAge()); //John's age
}
答案 13 :(得分:0)
否,您不需要进行条件查询,如果您在Spring-boot中工作,那么只需做一些简单的事情就可以了: 在您的存储库中,使用findBy [exact field name]声明一个方法名称。 例- 如果您的模型或文档包含一个字符串字段 myField ,而您想通过它来查找,则您的方法名称将为:
findBymyField(String myField);
答案 14 :(得分:-3)
使用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();
}