I have this Play Model class that I'm trying to modify an object of, and when I want to save it, I get the following exception:
java.lang.RuntimeException: No #javax.persistence.Id field found in class [class models.Contact]
at play.db.ebean.Model._idAccessors(Model.java:39)
at play.db.ebean.Model._getId(Model.java:52)
The class:
#Entity
public class Contact extends Model implements Person {//, Comparable<Contact>{
private Long id;
private Client client;
#Required
private String email;
private String profil_picture;
private Boolean active = new Boolean(true);
private Boolean favorite = new Boolean(false);
#Transient
private Boolean profile_pic_url_init = new Boolean(false);
#Id
#GeneratedValue
public Long getId() {
return id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="client_id")
public Client getClient(){
return client;
}
public void setClient(Client client){
this.client= client;
}
#Column
public Boolean getFavorite() {
return favorite;
}
public void setFavorite(Boolean is_favorite) {
this.favorite = is_favorite;
}
....
}
The code calling the save() method:
List<Contact> contacts_list = current_client.getContacts();
for (Contact c : contacts_list) {
c.setFavorite(false);
c.save();
}
The class actually has an #Id annotation, so any guesses of why this doesn't work? I tried looking it up on google, but couldn't find much about this error. Thanks in advance!
Move #Id annotation to id field instead of its getter.
Related
I try select data from the table by a filter with Spring Data JPA Specification I think what my implementation is correct, But it doesn't work. Help me please understand my mistake and fix my example.
I have very strange SQL query in log :
select phone0_.id as id1_0_, phone0_.note as note2_0_, phone0_.number as number3_0_, phone0_.operator_login as operator4_0_, phone0_.operator_pass as operator5_0_, phone0_.operator_name as operator6_0_, phone0_.operator_url as operator7_0_, phone0_.reg_date as reg_date8_0_, phone0_.status as status9_0_ from phone phone0_ where 0=1 limit ?
In the end: where 0=1 it's crash my mind. Where did that come from?
Here I fill CriteriaBuilder if filter field not null. I expect to get correctly built Specification object and send it to findAll(Specifications.where(specification), Pageable p) method. But something incorrect.
My repo and specification impl:
public interface PhoneRepository extends CrudRepository<Phone, Integer>, JpaRepository<Phone, Integer>, JpaSpecificationExecutor<Phone> {
class PhoneSpecification implements Specification<Phone> {
private final #NonNull PhoneService.PhoneFilter filter;
public PhoneSpecification(#NonNull PhoneService.PhoneFilter filter) {
this.filter = filter;
}
#Override
public Predicate toPredicate(Root<Phone> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.disjunction();
if (nonNull(filter.getId())) {
cb.disjunction().getExpressions().add(cb.equal(root.get("id"), filter.getId()));
}
if (nonNull(filter.getNote())) {
cb.disjunction().getExpressions().add(cb.like(root.get("note"), filter.getNote()));
}
if (nonNull(filter.getNumber())) {
cb.disjunction().getExpressions().add(cb.like(root.get("number"), filter.getNumber()));
}
if (nonNull(filter.getStatus())) {
cb.disjunction().getExpressions().add(cb.like(root.get("status"), filter.getStatus()));
}
if (nonNull(filter.getOpName())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorName"), filter.getOpName()));
}
if (nonNull(filter.getOpLogin())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorAccLogin"), filter.getOpLogin()));
}
if (nonNull(filter.getOpPassword())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorAccPassword"), filter.getOpPassword()));
}
if (nonNull(filter.getRegFrom()) && nonNull(filter.getRegTo())) {
cb.disjunction().getExpressions().add(cb.between(root.get("regDate"), filter.getRegFrom(), filter.getRegTo()));
}
return predicate;
}
}
}
This is service level:
#Service
public class PhoneService {
#Autowired
private PhoneRepository phoneRepository;
public Phone get(int id) {
Phone phone = phoneRepository.findOne(id);
return nonNull(phone) ? phone : new Phone();
}
public Page<Phone> list(#NonNull PhoneFilter filter) {
PhoneSpecification specification = new PhoneSpecification(filter);
return phoneRepository.findAll(Specifications.where(specification), filter.getPageable());
}
#Data
public static class PhoneFilter {
private Pageable pageable;
private Integer id;
private Timestamp regFrom;
private Timestamp regTo;
private String number;
private String opLogin;
private String opPassword;
private String opName;
private String status;
private String note;
}
}
And entity
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "phone")
#ToString(exclude = {"accounts"})
#EqualsAndHashCode(exclude = {"accounts"})
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToMany(mappedBy = "phone", cascade = CascadeType.DETACH)
private Collection<SocialAccount> accounts;
#Column(name = "reg_date")
private Timestamp regDate;
#Column(name = "number")
private String number;
#Column(name = "operator_url")
private String operatorUrl;
#Column(name = "operator_login")
private String operatorAccLogin;
#Column(name = "operator_pass")
private String operatorAccPassword;
#Column(name = "operator_name")
private String operatorName;
#Column(name = "status")
private String status;
#Column(name = "note")
private String note;
}
I find the mistake.
Method CriteriaBuilder.disjunction() this is factory and each time when I call him I got new Predicate object.
This implementation CriteriaBuilderImpl:
public Predicate disjunction() {
return new CompoundPredicate(this, BooleanOperator.OR);
}
Be careful with it.
Here are my entities ForfaitGenerique and Offre . Those two entities are in persistence.xml (didn't put all the methods here, if needed I will add more information):
#Entity
public class ForfaitGenerique implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int idForfait;
#NotNull
private String description = "description";
#NotNull
#OneToMany(cascade = {CascadeType.ALL},fetch = FetchType.LAZY,mappedBy = "forfaitGenerique")
private List<Offre> listeOffre;
#NotNull
#ElementCollection
List<Integer> listeRemontees;
//erreur sur mon intellij mais pas d'erreur en faisant mvn clean install.On verra au test
public ForfaitGenerique() {
}
public void addOffre(Offre o) {
this.listeOffre.add(o);
}
[...]
#Entity
public class Offre implements Serializable {
#NotNull
private AgeEnum age;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#NotNull
private double nbJour;
#Enumerated(EnumType.STRING)
#NotNull
private SaisonEnum saison;
#Enumerated(EnumType.STRING)
#NotNull
private ReductionEnum reduction;
boolean consecutif;
#NotNull
private double prix;
#ManyToOne
private ForfaitGenerique forfaitGenerique;
public Offre() {
}
Here is what I am trying to persist a ForfaitGenerique ; when I persist it and then try to retrieve it through its id (which isn't null and seems to have a good value), I get a ForfaitGenerique, which is not null, but ALL stuff it should contain is null (not the id though) :
#EJB(name="databaseAccess") protected ForfaitsInterface databaseAccess;
private ForfaitGenerique f;
private Offre offre_f;
[...]
ArrayList<Integer> l = new ArrayList<>();
l.add(1);
l.add(2);
l.add(3);
ForfaitGenerique forf = new ForfaitGenerique();
List<Offre> l_o = new ArrayList<Offre>();
forf.setListeOffre(l_o);
forf.setListeRemontees(l);
Offre o = new Offre(forf,AgeEnum.ADULTE,0.5,SaisonEnum.HAUTE,ReductionEnum.FIDELICIME,true,12.0);
forf.addOffre(o);
databaseAccess.addForfaitGenerique(forf);
int id_f = forf.getIdForfait();
assertNotNull(id_f);
System.out.println("bloublou"+id_f);
f = databaseAccess.getForfaitGenerique(id_f);
assertNotNull(f);
assertNotNull(f.getListeRemontees());//null !!
assertNotNull(f.getListeOffre());//null !!
assertEquals(f,forf);
offre_f = f.getListeOffre().get(0);
The databaseAccess object contains an entitymanager and two methods (among other), which are :
#Stateless(name="databaseAccess")
public class Forfaits implements ForfaitsInterface {
#PersistenceContext private EntityManager entityManager;
public void addForfaitGenerique(ForfaitGenerique forfaitGenerique) {
entityManager.persist(forfaitGenerique);
System.out.println("contains = "+entityManager.contains(forfaitGenerique));
}
#Override
public void addOffre(Offre o) {
entityManager.persist(o);
}
public ForfaitGenerique getForfaitGenerique(int id_forfait) {
ForfaitGenerique f = entityManager.find(ForfaitGenerique.class,id_forfait);//database.getForfaitFromId(id_forfait);
return f;
}
I think that my problem is when I am trying to persist my object, but not sure . Any help is appreciated .
In fact I just forgot to add the #Transactional(TransactionMode.COMMIT) before my test . That was the dumb solution.
I'm facing a problem with Ebean when trying to create Composed Keys (EmbeddedId).
This is a draft of what I have:
#Entity public class EntityA extends Model{
#Id
private String ID;
#OneToMany
private List<EntityB> listEntitesB;
public EntityA(){
ID = UUID.randomUUID();
}
}
#Entity public class EntityB extends Model{
#EmbeddedId
private EntityB_PK ID;
#ManyToOne
#JoinColumn(name="entityA_fk", referencedColumnName="listEntitiesB")
private EntityA entityA;
public EntityB(String entityB_ID){
ID = new EntityB_PK(UUID.randomUUID(), entityB_ID);
}
}
#Embeddable public class EntityB_PK{
private String entityB_ID;
private String entityA_FK_ID;
public EntityB_PK(String entB_ID, String entA_FK_ID){
entityB_ID = entB_ID;
entityA_FK_ID = entA_FK_ID;
}
}
Note: I'm using Ebean 4.1.4 with Java 1.6.
So, this code works well, but there is a perk, which I'm trying to solve - the resultant table in the database looks like this:
entityB_ID [primary]
entityA_FK_ID [primary]
entityA_fk
As you can see that last column is redundant considering the "entityA_FK_ID" column.
What I would like to have?
I would like to be able to tell Ebean to use the column "entityA_FK_ID" for the association #ManyToOne instead of creating it's own column.
To solve this problem we have to:
Map EntityB.entityA to the same column as EntityB.ID.entityA_FK_ID
set 'insertable' and 'updateable' attributes of EntityB.entityA #JoinColumn annotation
Override setter of EntityB.entityA
Here is the code:
EntityA.java:
#Entity
public class EntityA extends Model {
#Id
private String ID;
#OneToMany(mappedBy="entityA")
public List<EntityB> listEntitesB;
public static Finder<String,EntityA> find = new Finder<String,EntityA>(
String.class, EntityA.class
);
public EntityA() {
ID = UUID.randomUUID().toString();
}
public String getID() {
return ID;
}
}
EntityB.java:
#Entity
public class EntityB extends Model {
#EmbeddedId
private EntityB_PK ID;
#ManyToOne
#JoinColumn(name = "entityA_fk_id", insertable = false, updatable = false)
private EntityA entityA;
public EntityA getEntityA() {
return entityA;
}
public void setEntityA(EntityA aEntityA) {
entityA = aEntityA;
ID.entityA_FK_ID = aEntityA.getID();
}
public EntityB(String entityB_ID){
ID = new EntityB_PK(UUID.randomUUID().toString(), entityB_ID);
}
public String getID() {
return ID.entityB_ID;
}
}
EntityB_PK.java:
#Embeddable
public class EntityB_PK implements Serializable {
public String entityB_ID;
#Column(name="entityA_fk_id")
public String entityA_FK_ID;
public EntityB_PK(String entB_ID, String entA_FK_ID){
entityB_ID = entB_ID;
entityA_FK_ID = entA_FK_ID;
}
#Override
public int hashCode() {
return entityB_ID.length() + entityA_FK_ID.length();
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
EntityB_PK b = (EntityB_PK)obj;
if(b==null)
return false;
if (b.entityB_ID.equals(entityB_ID) && b.entityA_FK_ID.equals(entityA_FK_ID)) {
return true;
}
return false;
}
}
I have a CrudRepository that is supposed to make a query with an array (findByIn). In my repository tests it works, but when I try to use the query in my service, it doesn't work. Could someone explain why it doesn't work? Here is my setup (excluding some code irrelevant to the question)
Database model:
#Entity
#Table(name="Place")
public class Place implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "placeId", nullable = false)
private Long placeId;
#Column(name = "owner", nullable = false)
private String owner;
public Long getPlaceId() {
return placeId;
}
public void setPlaceId(Long placeId) {
this.placeId = placeId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
Repository:
#Repository
public interface PlaceRepository extends CrudRepository<Place, Long> {
List<Place> findByPlaceIdIn(Long[] placeId);
}
Service (this is the part not working):
#Service
public class PlaceService {
#Autowired
private PlaceRepository placeRepository;
public List<Place> getPlaces(Long[] placeIds) {
return placeRepository.findByPlaceIdIn(placeIds);
}
}
The problem is that in my service placeRepository.findByPlaceIdIn(placeIds) returns 0 objects if placeIds contains more than one item. If placeIds contains just one item, the query works fine. I tried replacing return placeRepository.findByPlaceIdIn(placeIds) with this piece of code that does the query for every array item one by one (this actually works, but I'd like to get the query work as it should):
ArrayList<Place> places = new ArrayList<Place>();
for (Long placeId : placeIds) {
Long[] id = {placeId};
places.addAll(placeRepository.findByPlaceIdIn(id));
}
return places;
I know that the repository should work, because I have a working test for it:
public class PlaceRepositoryTest {
#Autowired
private PlaceRepository repository;
private static Place place;
private static Place place2;
private static Place otherUsersPlace;
#Test
public void testPlacesfindByPlaceIdIn() {
place = new Place();
place.setOwner(USER_ID);
place2 = new Place();
place2.setOwner(USER_ID);
place = repository.save(place);
place2 = repository.save(place2);
Long[] ids = {place.getPlaceId(), place2.getPlaceId()};
assertEquals(repository.findByPlaceIdIn(ids).size(), 2);
}
}
I also have another repository for other model, which also uses findByIn and it works fine. I can't see any relevant difference between the repositories. I thought it might offer some more details to show the working repository, so I included it below:
Database model:
#Entity
#Table(name="LocalDatabaseRow")
#JsonIgnoreProperties(ignoreUnknown=false)
public class LocalDatabaseRow implements Serializable {
public LocalDatabaseRow() {}
public LocalDatabaseRow(RowType rowType) {
this.rowType = rowType;
}
public enum RowType {
TYPE1,
TYPE2
};
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#JsonProperty("id")
private Long id;
#JsonProperty("rowType")
#Column(name = "rowType")
private RowType rowType;
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public RowType getRowType() {
return rowType;
}
public void setRowType(RowType rowType) {
this.rowType = rowType;
}
}
Repository:
#Repository
public interface LocalDatabaseRowRepository extends CrudRepository<LocalDatabaseRow, Long> {
List<LocalDatabaseRow> findByRowTypeAndUserIdIn(RowType type, String[] userId);
}
try using a list instead :
findByPlaceIdIn(List placeIdList);
You have a typo in your code (the repository declaration in the service):
#Autowired
private placeRepository placeRepository;
Should be:
#Autowired
private PlaceRepository placeRepository;
I want to map the following classes with Hibernate JPA:
My code looks like this:
#Entity
public class Grid{
#Id
#GeneratedValue
private Long id;
#Column(unique=true)
private String name;
private String location;
private BigDecimal costsPerCPUMinute;
#OneToMany(mappedBy="grid")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#Entity
public class User extends Person{
#Column(nullable=false, unique=true)
private String username;
#Column(length=16,columnDefinition="BINARY(16)")
private byte[] password;
#OneToMany(mappedBy="user")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#Id
#ManyToOne
private Grid grid;
#Id
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Unfortunately, I get the following Exception:
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: dst1.model.Membership.grid in dst1.model.Grid.mem
As far as I understand the message, grid cannot be found in Membership. But as you can see in de code, there definitly is a variable named grid in the Membership class.
Does anybody has an idea whats going wrong?
Update: As suggested in the comments, I also tried to change the Membership Class by using IDClass or EmbeddedID. The EmbeddedID version looks like this:
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#EmbeddedId
private MembershipPK membershipPK;
public Membership(){};
public MembershipPK getMembershipPK() {
return membershipPK;
}
public void setMembershipPK(MembershipPK membershipPK) {
this.membershipPK = membershipPK;
}
#SuppressWarnings("serial")
#Embeddable
public class MembershipPK implements Serializable{
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Unfortunately, I still get the same exception.
Update 2: I will rewrite all three classes from scratch tomorrow evening and update this post if that changes anything.
You should be able to use something like
#Embeddable
public class MembershipId
{
protected Grid grid;
protected User user;
}
#Entity
public class Membership {
#EmbeddedId
MembershipId id;
}
#Entity
public class User {
#OneToMany(mappedBy="id.user")
private Set<Membership> memberships = new HashSet<Membership>();
}
From the top of my head: shouldn't this be rather
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
#IdClass(MembershipId.class)
public class Membership implements Serializable {
#Id #ManyToOne
private Grid grid;
#Id #ManyToOne
private User user;
// rest of class
}
Edit: What the exception is telling you is that your Grid class has a field named mem and that the entity represented by this field needs a grid field, but doesn't have one. Here is where your Grid.mem needs a grid field:
#Entity
public class Grid{
...
#OneToMany(mappedBy="grid")
private List<Membership> mem;
This mapping can only work if there is a property grid in Membership.class. If you hide the grid inside the IdClass, Grid.mem can't find it. You might try this:
#Embeddable
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
public class Membership implements Serializable {
#EmbeddedId
private MembershipId id;
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
// rest of class
}