I'm struggling to successfully implement a OneToOne mapping on my play framework application.
examples I have are:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public static int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public static User user;
}
and :
#Entity
public class User extends Model {
#Required
public String firstName;
#Required
public String surname;
}
in this setup it throws:
org.hibernate.AnnotationException: No identifier specified for entity: models.Profile
EDIT: As per Christian Boariu's answer, I have modified Profile to what you have suggested and User to:
#Entity
public class User extends GenericModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long user_id;
#Required
public String firstName;
#Required
public String surname;
#OneToOne(cascade = {CascadeType.ALL})
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "profile_id")
public Profile profile;
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}
Also added getter/setter to Profile:
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
But I am now getting hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile.. not sure how to correct?
The problem is about your Id definition.
It should not be static.
Also, user should not be static as well.
UPDATE:
So your class should be like this:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public User user;
}
Fixed. suggesstions, as above fixed the #OneToOne issue and the hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile was due to trying to persist an entity with a null id - due to using #primaryKeyJoin, so changed to #JoinColumn
Related
This question is already phrased as an issue here: https://github.com/spring-projects/spring-data-jpa/issues/2369 but for lack of a reaction there I am copying the contents of that issue here, hoping that somebody might find what's wrong with my code or confirm that this could be a bug:
I've set up an example project here that showcases what seems to be a bug in Spring Data projections: https://github.com/joheb-mohemian/gs-accessing-data-jpa/tree/primary-key-join-column-projection-bug/complete
I have a Customer entity that has a OneToOne mapping to an Address entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
//...
}
#Entity
public class Address {
#Id
#Column(name = "customer_id")
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "customer_id")
private Customer customer;
private String street;
//...
}
Then there are simple projection interfaces:
public interface CustomerProjection {
String getFirstName();
String getLastName();
AddressProjection getAddress();
}
public interface AddressProjection {
String getStreet();
}
But when I try to fetch a projected entity from a repository method like this one:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
//...
<T> T findById(long id, Class<T> type);
}
, getAddress() on the projection will be null, whereas getAddress() when fetching the entity type is populated correctly. Of these two unit tests, only testEntityWithOneToOne()will be successful:
#BeforeEach
void setUpData() {
customer = new Customer("first", "last");
Address address = new Address(customer, "street");
customer.setAddress(address);
entityManager.persist(address);
entityManager.persist(customer);
}
#Test
void testEntityWithOneToOne() {
Customer customerEntity = customers.findById(customer.getId().longValue());
assertThat(customerEntity.getAddress()).isNotNull();
}
#Test
void testProjectionWithOneToOne() {
CustomerProjection customerProjection = customers.findById(customer.getId(), CustomerProjection.class);
assertThat(customerProjection.getAddress()).isNotNull();
}
What's the problem here?
I want to get a list of users by username, with enabled status and has phone number. I managed to get it working till I add the phone number parameter.
here is my code:
//crud repo
List<Users> findAllByUserNameContainsAndEnabledIsAndMobileNotNull(String userName, String enabled);
// controller
public Set<User> searchByName(#PathVariable String username) throws Exception {
Set<User> result = new HashSet<>();
result.addAll(userRepository.findAllByUserNameContainsAndEnabledIsAndMobileNotNull(username, "Y"));
return result;
}
// user class
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(precision = 18, scale = 0)
private BigDecimal id;
#Column(name = "Enabled", columnDefinition = "char(1) default 'Y'")
private String enabled;
private String username;
private String mobile;
// getters setters..
What is your phone number parameter called? You said it was working until that. The names have to match.
Here is all supported keywords in the method names:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
I have started new project and everything works as expected, so the problem is not in method name. Here is code I've tested:
Entity
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(precision = 18, scale = 0)
private BigDecimal id;
#Column(name = "Enabled", columnDefinition = "char(1) default 'Y'")
private String enabled;
private String userName;
private String mobile;
public User(String enabled, String userName, String mobile) {
this.enabled = enabled;
this.userName = userName;
this.mobile = mobile;
}
}
Repositiory
public interface UserRepository extends CrudRepository<User, BigDecimal> {
List<User> findAllByUserNameContainsAndEnabledIsAndMobileNotNull(String userName, String enabled);
}
Test
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
class UserRepositoryTest {
#Autowired
private UserRepository userRepository;
#Test
void test_findAllByUserNameContainsAndEnabledIsAndMobileNotNull() {
User user1 = new User("Y", "user1", "mobile");
User user2 = new User("Y", "user2", null);
userRepository.save(user1);
userRepository.save(user2);
List<User> all = userRepository.findAllByUserNameContainsAndEnabledIsAndMobileNotNull("user", "Y");
System.out.println("all = " + all);
}
}
And only one record in the output:
all = [User(id=1, enabled=Y, userName=user1, mobile=mobile)]
To find an object from entity with primary key we use em.find(Person.class, <Id>).
I'm using JPA EclipseLink and I have a Person entity which has a composite primary key(#classId),
the Person entity:
#Entity
#IdClass(PersonId.class)
public class Person {
#Id
private int id;
#Id
private String name;
public String getName() {
return name;
}
// getters & setters
}
and the PersonID:
public class PersonId implements Serializable {
private static final long idVersionUID = 343L;
private int id;
private String name;
// must have a default construcot
public PersonId() {
}
public PersonId(int id, String name) {
this.id = id;
this.name = name;
}
//getters & setters
//hachCode & equals
}
How to use em.find to get a Person object?
I found the solution :
PersonId personeId = new PersonId(33, "Jhon");
Person persistedPerson = em.find(Person.class, personeId);
System.out.println(persistedPerson.getID() + " - " + persistedPerson.getName());
I am stuck with this error message, that appears every time I want to add a ManytoOne relationship with another entity class.
The class must use a consistent access type (either field or property). There is no ID defined for this entity hierarchy
This is my entity Transaction
#Entity
#Table(name = "CustomerTransaction")
public class CustomerTransaction implements Serializable {//this is the line with the error message
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne //This generates the problem
#JoinColumns({
#JoinColumn(name = "CUS_ID", referencedColumnName = "IDCUSTOMER") })
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private long transactionID;
#Temporal(TemporalType.TIMESTAMP)
private Date buyDate;
public Date getBuyDate() {
return buyDate;
}
public void setBuyDate(Date buyDate) {
this.buyDate = buyDate;
}
public long getTransactionID() {
return transactionID;
}
public void setTransactionID(long transactionID) {
this.transactionID = transactionID;
}
public String getCarYear() {
return carYear;
}
public void setCarYear(String carYear) {
this.carYear = carYear;
}
public Date getTransactionDate() {
return transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
private String carYear;
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate;
JPA annotation should all be placed either on fields or on accessor methods. You've placed the #Id and #GeneratedValue annotation on a field (private Long id), but #ManyToOne and #JoinColumns on a getter (public Long getId()). Move the latter on a field as well.
i had similar error but in the end, i realized #Id was referencing this package org.springframework.data.annotation.Id instead of javax.persistence.Id. i was using #MappedSuperClass approach so as soon as i corrected this, everything worked fine
You need to import #Id from "import javax.persistence.Id;"
I need 3 entities: User, Contract (which are a many to many relation) and a middle entity: UserContract (this is needed to store some fields).
What I want to know is the correct way to define the relationships between these entities in JPA/EJB 3.0 so that the operations (persist, delete, etc) are OK.
For example, I want to create a User and its contracts and persist them in a easy way.
Currently what I have is this:
In User.java:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<UserContract> userContract;
In Contract.java:
#OneToMany(mappedBy = "contract", fetch = FetchType.LAZY)
private Collection<UserContract> userContract;
And my UserContract.java:
#Entity
public class UserContract {
#EmbeddedId
private UserContractPK userContractPK;
#ManyToOne(optional = false)
private User user;
#ManyToOne(optional = false)
private Contract contract;
And my UserContractPK:
#Embeddable
public class UserContractPK implements Serializable {
#Column(nullable = false)
private long idContract;
#Column(nullable = false)
private String email;
Is this the best way to achieve my goals?
Everything looks right. My advice is to use #MappedSuperclass on top of #EmbeddedId:
#MappedSuperclass
public abstract class ModelBaseRelationship implements Serializable {
#Embeddable
public static class Id implements Serializable {
public Long entityId1;
public Long entityId2;
#Column(name = "ENTITY1_ID")
public Long getEntityId1() {
return entityId1;
}
#Column(name = "ENTITY2_ID")
public Long getEntityId2() {
return entityId2;
}
public Id() {
}
public Id(Long entityId1, Long entityId2) {
this.entityId1 = entityId1;
this.entityId2 = entityId2;
}
}
protected Id id = new Id();
#EmbeddedId
public Id getId() {
return id;
}
protected void setId(Id theId) {
id = theId;
}
}
I omitted obvious constructors/setters for readability. Then you can define UserContract as
#Entity
#AttributeOverrides( {
#AttributeOverride(name = "entityId1", column = #Column(name = "user_id")),
#AttributeOverride(name = "entityId2", column = #Column(name = "contract_id"))
})
public class UserContract extends ModelBaseRelationship {
That way you can share primary key implementation for other many-to-many join entities like UserContract.