MyBatis - One to many - values not set for mapped column - java

I am using MyBatis to access the database.
For that purpose I have the following classes:
class ClassA {
private int id;
private List<ClassB> list;
// public getters and setters
}
class ClassB {
private int id;
// public getters and setters
}
The according DAOs look like that:
public interface ClassADAO {
#Select("SELECT id, name, description FROM TableA WHERE id = #{id}")
#Results(
#Result(property = "list", javaType = List.class, column = "id",
many = #Many(select = "ClassBDao.getClassBForClassA")))
ClassA getClassAById(#Param("id") long id);
}
public interface ClassBDAO {
#Select("SELECT id, classAId FROM TableB WHERE classAId = #{id}")
ClassB getClassBForClassA(#Param("id") long id);
}
Unfortunately the id column of ClassA is not filled with the correct id.
It seems that this is because it is used as a mapped column.
Anyone already experienced this problem or has a solution? Even renaming of columns would not help as far as I can see it, because it will still be a mapped column and by consequence the value will not be set.
I was able to track it down in the mybatis code I think:
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyAutomaticMappings() does only apply the mappings for unmapped columns.

I found the solution for all that may struggle with the same problem in the future.
Strangely you have to specify the id column as additional result (as it is mapped):
public interface ClassADAO {
#Select("SELECT id, name, description FROM TableA WHERE id = #{id}")
#Results({#Result(property = "id", column = "id"),
#Result(property = "list", javaType = List.class, column = "id",
many = #Many(select = "ClassBDao.getClassBForClassA"))})
ClassA getClassAById(#Param("id") long id);
}

What I wound up doing was to have a separate mapper and method for the parent class, without the children. After I got the fully-populated object from the mapper, I made a second call to get just the parent class (with the ID), then simply copy the ID to the fully-populated object. Brute force and awkwardness FTW!
ClassA a;
try (SqlSession session = DBConfig.getSessionFactory().openSession()) {
ClassAMapper mapper = session.getMapper(ClassAMapper.class);
a = (mapper.getA(id));
ClassA a2 = (mapper.getBaseInfo(id));
a.setID(a2.getID());
}

Related

NativeQuery Spring Data return object

I need to implement a query in Spring Data like this :-
Select User.name, sum(Activity.minutes)
From User, Activity, ActivityStatus
Where User.id = ActivityStatus.userId
And Activity.id = ActivityStatus.activityId
AND ActivityStatus = "COMPLETED"
GROUP BY user.name;
So i need to join 3 tables, therefore I have to use #Query with nativeQuery = true ( correct me if I'm wrong here )
And so my Repository method looks like this :-
#Query(value = "Select User.name, sum(Activity.minutes) as total_minutes
From User, Activity, ActivityStatus
Where User.id = ActivityStatus.userId
And Activity.id = ActivityStatus.activityId
AND ActivityStatus = "COMPLETED"
AND User.Type = ?1
GROUP BY user.name;",
nativeQuery = true
)
List<MyObj> getTotalActivityMinutesByUserType(String userType);
MyObj class looks like this :-
public class MyObj {
String name;
long total_minutes;
// getter and setter methods
public MyObj(String name, long total_minutes) {
this.name = name;
this.total_minutes = total_minutes;
}
}
My Test Method :-
#Test
public void TotalActivityTest() throws Exception {
List<MyObj> objA = myRepository.getTotalActivityMinutesByUser("TEST");
}
and i get the following exception :-
org.springframework.core.convert.ConversionFailedException: Failed to
convert from type [java.lang.Object[]] to type
[com.mycomp.MyObj] for value '{TEST, 5.0}'; nested
exception is
org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type [java.lang.String] to
type [com.mycomp.dto.MyObj]
I need a way to return the result as MyObj. ( Or at least a way to cast it to MyObj) Is this possible?
EDIT:
Building from #Cepr0's answer My Entity class looks like this :-
#Entity
public class ActivityStatus extends Base {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Activity activity;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private User user;
}
I am not sure how the JOIN query in JPQL should look like...
Just use Projection and JPQL query:
public interface NameAndDuration {
String getName();
Long getDuaration();
}
#Query("select u.name as name, sum(a.minutes) as duration from User u join u.activityStatus st join st.activity a where st.status = "COMPLETED" and u.type = ?1 group by u.name")
List<NameAndDuration> getNameAndDurationByUserType(String userType);
List<NameAndDuration> list = getNameAndDurationByUserType("TEST");
String userName = list.get(0).getName();
This query is probably not exact what you need because I don't know a structure of your entity classes. But if you show them I will correct the query...
The query is returning object array which you are storing in an normal object.Iterate through object array and set it to object like below
List<MyObj > test= new ArrayList<>();
List<Object[]> rows= query.list();
for (Object[] row : rows) {
MyObj temp=new MyObj (arg1,arg2);
temp.set((Dataype) row[0])//You need to create getters and setters for your pojo
..
test.add(temp);
}
I actually had a similar issue with this exception. I have 3 tables: Project, Asset, and ProjectAsset. ProjectAsset is the reference table where one project can have many assets. So I created 3 repositories, one for each entity. The problem is I placed my #Query in the ProjectAssetRepository and that didn't work due to the repository extending CrudRepository. ProjectAssetId is an embedded id made up of projectid and assetid. I can't just return Asset objects in this repository so I moved the method to AssetRepository and everything worked. If you are using cross-reference tables, make sure you pull the correct object or else you will run into this exception.

Mapping Custom Named/Native Queries to Entities

It's possible mapping custom native/named queries to entities? I have something like this
NamedQueries({
NamedQuery(name = "StateBo.findByCountry", query = "SELECT state FROM StateBo state WHERE state.country.id = ?"),
NamedQuery(name = "StateBo.showIdfindByCountry", query = "SELECT state.id FROM StateBo state WHERE state.country.id = ?")
})
#Table(name = "STATE")
#Entity(name = "StateBo")
public class StateBo extends BaseNamedBo {
private static final long serialVersionUID = 3687061742742506831L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STATE_ID")
private Long id;
#Column(name = "ISO_CODE")
private String isoCode;
#ManyToOne
#JoinColumn(name = "COUNTRY_ID")
private CountryBo country;
// getters and setters ...
}
I have my method to call Native/Named queries like this.
#Override
public List<E> executeQuery(String queryName, List<Object> criteria) {
TypedQuery<E> query = entityManager.createNamedQuery(queryName, entityClass);
Integer argumentPosition = 1;
if ( (criteria != null) && (criteria.size() > 0) ){
for(Object object : criteria) {
query.setParameter(argumentPosition, object);
argumentPosition++;
}
}
return (List<E>) query.getResultList();
}
When I call the StateBo.findByCountry the result is mapped to StateBo, but if I call StateBo.showIdfindByCountry the result is not mapped to StateBo because I'm only selected on the query the state.id instead of the fields on the table.
I don't want to select all the fields of the STATE table, I only want in this case the state.id, but when I customize my native query, the result is not mapped to StateBo instead of this, the result is a Long type.
My question is, Is possible map to an Entity the result of StateBo.showIdfindByCountry? I case that I have more fields like state.isoCode, is possible map to StateBo, the custom query? or only is possible if I return all the fields from the query, like the first query StateBo.findByCountry
It is possible, but as JB Nizet said - "your collegues will suffer from such a design decision".
Anyway, in order to do that you should create custom constructor in your entity class. This constructor should accept Long argument and assign it to id field of your entity class.
Then you should change your query to include NEW keyword followed by full qualified entity class name as below:
SELECT NEW your.package.StateBo(sb.id)
FROM StateBo sb
WHERE state.country.id = ?
Please note that all entities retreived from database in such a way will not be managed by persistence context.

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

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 based on some field other than ID. How do we obtain the entity in that case (since em.find() cannot be used).
I understand we can use a query and filter the results later. But, is there a more direct way (because this is a very common problem as I understand).
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.
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.
Basically, you should add a specific unique field. I usually use xxxUri fields.
class User {
#Id
// automatically generated
private Long id;
// globally unique id
#Column(name = "SCN", nullable = false, unique = true)
private String scn;
}
And you business method will do like this.
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;
}
}
Best practice is using #NaturalId annotation. It can be used as the business key for some cases it is too complicated, so some fields are using as the identifier in the real world.
For example, I have user class with user id as primary key, and email is also unique field. So we can use email as our natural 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;
}
To get our record, just simply use 'session.byNaturalId()'
Session session = sessionFactory.getCurrentSession();
User user = session.byNaturalId(User.class)
.using("email","huchenhai#qq.com")
.load()
This solution is from Beginning Hibernate book:
Query<User> query = session.createQuery("from User u where u.scn=:scn", User.class);
query.setParameter("scn", scn);
User user = query.uniqueResult();
I solved a similar problem, where I wanted to find a book by its isbnCode not by your id(primary key).
#Entity
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String isbnCode;
...
In the repository the method was created like #kamalveer singh mentioned. Note that the method name is findBy+fieldName (in my case: findByisbnCode):
#Repository
public interface BookRepository extends JpaRepository<Book, Integer> {
Book findByisbnCode(String isbnCode);
}
Then, implemented the method in the service:
#Service
public class BookService {
#Autowired
private BookRepository repo;
public Book findByIsbnCode(String isbnCode) {
Book obj = repo.findByisbnCode(isbnCode);
return obj;
}
}
Write a custom method like this:
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;
}
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.
No, you don't need to make criteria query it would be boilerplate code you just do simple thing if you working in Spring-boot:
in your repo declare a method name with findBy[exact field name].
Example-
if your model or document consist a string field myField and you want to find by it then your method name will be:
findBymyField(String myField);
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 query = session.createQuery(cr);
query.setMaxResults(1);
List<Person> resultList = query.getResultList();
Person result = resultList.get(0);
return result;
}
}
example of use:
public static void main(String[] args) {
Person person = Dao.getPersonByName("John");
System.out.println(person.getAge()); //John's age
}
Have a look at:
JPA query language: The Java Persistence Query Language
JPA Criteria API: Using the Criteria API to Create Queries
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
Refer - Spring docs for query methods
We can add methods in Spring Jpa by passing diff params in methods like:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
In my Spring Boot app I resolved a similar type of issue like this:
#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;
}
This is very basic query :
Entity : Student
#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;
}
Repository Interface : 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);
}
Note:
findByColumnName : give results by criteria
List findByStudentName(String studentName)
Internally convert into query : select * from Student where name='studentName'
#Transactional
#Modifying
Is useful when you want to remove persisted data from database.
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();
}

Complex Hibernate Projections

I want to ask, it is possible that I create query projections and criterion for more than one level deep?
I have 2 model classes:
#Entity
#Table(name = "person")
public class Person implements Serializable {
#Id
#GeneratedValue
private int personID;
private double valueDouble;
private int valueInt;
private String name;
#OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
#JoinColumn(name="wifeId")
private Wife wife;
/*
* Setter Getter
*/
}
#Entity
#Table(name = "wife")
public class Wife implements Serializable {
#Id
#GeneratedValue
#Column(name="wifeId")
private int id;
#Column(name="name")
private String name;
#Column(name="age")
private int age;
/*
* Setter Getter
*/
}
My Criteria API :
ProjectionList projections = Projections.projectionList();
projections.add(Projections.property("this.personID"), "personID");
projections.add(Projections.property("this.wife"), "wife");
projections.add(Projections.property("this.wife.name"), "wife.name");
Criteria criteria = null;
criteria = getHandlerSession().createCriteria(Person.class);
criteria.createCriteria("wife", "wife", JoinType.LEFT.ordinal());
criterion = Restrictions.eq("wife.age", 19);
criteria.add(criterion);
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class));
return criteria.list();
and I hope, I can query Person, with specified criteria for wife property, and specified return resultSet.
so i used Projections for getting specified return resultSet
I want personID, name(Person), name(Wife) will returned. how API i must Use, i more prefer use Hibernate Criteria API.
This time, I used code above for getting my expected result, but it will throw Exception with error message :
Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person,
and whether my Restrictions.eq("wife.age", 19); is correct for getting person which has wife with 19 as her age value ?
Thanks
AFAIK it is not possible to project more than one level deep with aliastobean transformer. Your options are
create a flattened Data Transfer Object (DTO)
fill the resulting Person in memory yourself
implement your own resulttransformer (similar to option 2)
option 1 looks like this:
Criteria criteria = getHandlerSession().createCriteria(Person.class)
.createAlias("wife", "wife", JoinType.LEFT.ordinal())
.add(Restrictions.eq("wife.age", 19));
.setProjection(Projections.projectionList()
.add(Projections.property("personID"), "personID")
.add(Projections.property("name"), "personName")
.add(Projections.property("wife.name"), "wifeName"));
.setResultTransformer(Transformers.aliasToBean(PersonWifeDto.class));
return criteria.list();
I wrote the ResultTransformer, that does this exactly. It's name is AliasToBeanNestedResultTransformer, check it out on github.
Thanks Sami Andoni. I was able to use your AliasToBeanNestedResultTransformer with a minor modification to suit my situation. What I found was that the nested transformer did not support the scenario where the field is in a super class so I enhanced it to look for fields up to 10 levels deep in the class inheritance hierarchy of the class you're projecting into:
public Object transformTuple(Object[] tuple, String[] aliases) {
...
if (alias.contains(".")) {
nestedAliases.add(alias);
String[] sp = alias.split("\\.");
String fieldName = sp[0];
String aliasName = sp[1];
Class<?> subclass = getDeclaredFieldForClassOrSuperClasses(resultClass, fieldName, 1);
...
}
Where getDeclaredFieldForClassOrSuperClasses() is defined as follows:
private Class<?> getDeclaredFieldForClassOrSuperClasses(Class<?> resultClass, String fieldName, int level) throws NoSuchFieldException{
Class<?> result = null;
try {
result = resultClass.getDeclaredField(fieldName).getType();
} catch (NoSuchFieldException e) {
if (level <= 10){
return getDeclaredFieldForClassOrSuperClasses(
resultClass.getSuperclass(), fieldName, level++);
} else {
throw e;
}
}
return result;
}
My Hibernate projection for this nested property looked like this:
Projections.projectionList().add( Property.forName("metadata.copyright").as("productMetadata.copyright"));
and the class I am projecting into looks like this:
public class ProductMetadata extends AbstractMetadata {
...
}
public abstract class AbstractMetadata {
...
protected String copyright;
...
}
Instead of creating Data Transfer Object (DTO)
In projectionlist make below changes and it will work for you.
ProjectionList projections = Projections.projectionList();
projections.add(Projections.property("person.personID"), "personID");
projections.add(Projections.property("person.wife"), "wife");
projections.add(Projections.property("wife.name"));
Criteria criteria = null;
criteria = getHandlerSession().createCriteria(Person.class,"person").createAlias("person.wife", "wife");
criterion = Restrictions.eq("wife.age", 19);
criteria.add(criterion);
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class));
return criteria.list();

Best Practices for JPA - joining read-only data

I am using a JPA model with two classes. The first one is mapping a table with "dynamic" data, the second one is mapping a table with read-only, reference data.
As an example, I have a Person entity mapping a Person Table, that contains a #OneToOne reference to the Civility entity, which itself maps to the Civility table (2 columns) that only has 3 records in it (Miss, Mrs and Mr).
I wanted to know the best way to write a query on the person entity based on Civility value. For example, what query would I use to get all Person's with civility = Mr?
Thanks.
one way to map reference lookup data is to use the #Enumerated annotation in jpa. You still have to create enumeration with the lookup values, but that's why it's reference data anyway.
For example, I have a rating code, and its a string/varchar value on table.
But can use a enumeration to use it:
#Enumerated(EnumType.STRING)
#Column
public RatingCode getRating() {
return rating;
}
public void setRating(RatingCode rating) {
this.rating = rating;
}
and the enumeration is:
public enum RatingCode {
Core, Star
}
Use a unit test to try all values, and you know it's a safe way to get reference data.
You can still use HQL to pull out the values, and pass the enumeration as the value:
hql = "select r from Rating as r where r.rating = :aEnum"
// and in the call to pass the parameter
qry.setParameter("aEnum", aRatingCode)
The enumeration is a field within the Rating entity:
#Entity
#Table
public class Rating {
private Integer rating_Id;
private RatingCode rating;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column
public Integer getRating_Id() {
return rating_Id;
}
public void setRating_Id(Integer rating_Id) {
this.rating_Id = rating_Id;
}
#Enumerated(EnumType.STRING)
#Column
public RatingCode getRating() {
return rating;
}
public void setRating(RatingCode rating) {
this.rating = rating;
}
}
So I have a profile, that requires a Rating, so I lookup a rating via the enumeration and add it to the profile.
Profile p = new Profile();
RatingServiceI rs = new RatingService()
Rating r = rs.getRating(RatingCode.Core);
p.setRating(r);
You didn't post your entity definitions, so you will need to interpret the code in this answer to match up with your actual models. Also, note that querying the entities themselves, in this case, has nothing to do whether the data in the underlying tables is 'read-only' or not:
final String queryStr = "SELECT p FROM Person p WHERE p.civility.value = :value";
final TypedQuery<Person> query = entityManager.createQuery(queryStr, Person.class);
query.setParameter("value", "Mr");
List<Person> results = query.getResultList();

Categories

Resources