I have setup two entities like below in a one-to-one mapping and I am trying to query on the joincolumn like below in my repository:
#Entity
#Table(name = "a")
#AllArgsConstructor
#NoArgsConstructor
#Data
#EqualsAndHashCode(callSuper=false)
public class A extends Auditable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
#Size(min = 1, max = 100)
private String name;
}
#Entity
#Table(name = "b")
#AllArgsConstructor
#NoArgsConstructor
#Data
#EqualsAndHashCode(callSuper=false)
public class B extends Auditable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
#Size(min = 1, max = 100)
private String name;
#OneToOne
#JoinColumn(name="aId", referencedColumnName = "aId")
private A aId;
}
In my BRepository.java, I am trying to do this:
#Component
public interface BRepository extends JpaRepository<B, Long> {
List<B> findAllBByA_aId(String aId);
}
I get the following error:
No property a found for type B! Did you mean 'aId'?
Is this not the right way to query on a join column in spring-data??
Since you have not defined the column name for id in A then the column name will defaults to id. Then in class B, you should change the referencedColumnName to id (or else you can simply skip the referencedColumnName attribute since it can be derived directly from the target entity in an OneToOne relationship)
#Entity
#Table(name = "b")
#AllArgsConstructor
#NoArgsConstructor
#Data
#EqualsAndHashCode(callSuper=false)
public class B extends Auditable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
#Size(min = 1, max = 100)
private String name;
#OneToOne
#JoinColumn(name="aId", referencedColumnName = "id")
private A aId;
}
In repository, you need to annotate it with #Repository annotation to let Spring know it should be treated as a Repository Bean.
#Repository
public interface BRepository extends JpaRepository<B, Long> {
#Query(value="select b from B b where B.aId.id=?1")
List<B> findAllBByA_aId(String aId);
}
or you can use SPeL directly,
#Repository
public interface BRepository extends JpaRepository<B, Long> {
List<B> findAllByaIdId(String aId);
}
Related
I have these entities where Shop entity is parent:
#Data
#NoArgsConstructor
#Entity
#DynamicUpdate
#Table(name = "Shop", schema = "public")
public class ShopDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String processedStatus;
#OneToMany(mappedBy = "shopDao", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookDao> bookDaoList;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"shopDao"})
#Table(name = "Book", schema = "public")
public class BookDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String author;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "other_id", referencedColumnName = "id")
private OtherDao otherDao;
#ManyToOne
#JoinColumn(name = "shop_id", nullable = false)
private ShopDao shopDao;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"bookDao"})
#Table(name = "Other", schema = "public")
public class OtherDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String metadata;
#OneToOne(mappedBy = "otherDao", fetch = FetchType.EAGER)
private BookDao bookDao;
}
And these are repos:
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
#Repository
public interface BookRepo extends JpaRepository<BookDao, Long> {
}
#Repository
public interface OtherRepo extends JpaRepository<OtherDao, Long> {
}
When i'm using findAllByProcessedStatus() function, i get BookList inside Shop object correctly, but each Book can't reach their Other objects and i get LazyInitializationException:
screenshot
How do i fix that problem?
Actually, with spring data's #EntityGraph all you need is :
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList.otherDao"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
This is the most convenient way.
For more complex relations, you could define a #NamedEntityGraph, and provide subgraphs, like so.
What I find intriguing, is that the BookDao is the owner of this relation, so I would expect it to be eagerly loaded, since you haven't specified a the Lazy fetch mode explicitly ...
I'm trying to build build service, which saves object with sub-objects, but getting error. In result object data fields saved, but sub-object not.
I have the next object. The main is Order and sub-object is Partner:
#Getter
#Setter
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private int orderId;
#OneToMany(mappedBy = "order", fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private Set<Partner> partners;
}
#Getter
#Setter
#Entity
#Table(name = "partners")
public class Partner implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "partner_id")
private int id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
I use standard embedded method "save" from Spring Jpa Repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Integer> {
}
and service, which call this Repository:
#Service
public class OrdersServiceImpl implements OrdersService {
#Autowired
private OrdersRepository repository;
#Override
public Order save(Order order) {
return repository.save(order);
}
}
Does someone have an idea why Partners are not saved?
Thanks a lot!
Because the relationship owner is Partner, so that you need to save the Order first. Or you can put cascade = CascadeType.PERSIST on private Order order;
Spring boot 2.2.2 ~ 2.2.4 ( the one i have tested so far)
I have an Abstract class
#MappedSuperclass
#Data
#EntityListeners(AuditingEntityListener.class)
#FilterDef(name = "tenantFilter", parameters = {#ParamDef(name = "tenantId", type = "int")})
#Filter(name = "tenantFilter", condition = "tenantId = :tenantId")
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
....
}
A personnel class extending the abstract class
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "personnel")
#AllArgsConstructor
#NoArgsConstructor
#Data
#EntityListeners(AuditingEntityListener.class)
public class Personnel extends BaseEntity {
#NotNull
#OneToOne
#JoinColumn(name = "titleID")
private Title title;
// other fields and relationships of type ManyToOne, OneToMany
}
My Jpa Repository class
public interface PersonnelRepository extends JpaRepository<Personnel, Integer> {
Optional<PersonnelData> findByFileNo(String fileNo);
Optional<PersonnelData> findByIdAndDeletedOnIsNull(Integer id);
}
In my database, I have personnel records with some of the fields being null and the relationships.
In my controller when i do findById(1), it returns null but when I do findByIdAndDeletedOnIsNull(1), it returns the record.
I have tried #NotFound(action = NotFoundAction.IGNORE) on the relationships that are null with no success.
I have 3 entities BaseFoo, FooAbc, FooAbcDetail. FooAbc extends base entity BaseFoo. I'm trying to do one to one relation between FooAbc and FooAbcDetail.
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "foo")
#Audited
#AuditOverride(forClass = Auditable.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class BaseFoo extends Auditable implements Serializable {
#Id
#Column(name = "id")
private Long id;
//other fields
}
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Audited
#AuditOverride(forClass = BaseFoo.class)
#DiscriminatorValue("2")
public class FooAbc extends BaseFoo implements Serializable {
#EqualsAndHashCode.Exclude
#ToString.Exclude
#NotAudited
#OneToOne(mappedBy = "fooAbc",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true,
optional = false)
private FooAbcDetail fooAbcDetail;
//other fields
}
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "foo_id"/* foo_abc_id (I tried both) */)
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#MapsId //also tried #MapsId("id")
#OneToOne(fetch = FetchType.LAZY)
private FooAbc fooAbc; //also tried BaseFoo
//other fields
}
While project starting up Hibernate throws:
org.hibernate.MappingException: Unable to find column with logical
name: id in org.hibernate.mapping.Table(public.foo_abc_detail) and its
related supertables and secondary tables
What is the problem here?
Environment
Hibernate 5.3.10.Final
Spring Boot 2.1.7.RELEASE
This error is telling you that there is no column on the foo_abc_detail table called foo_id.The column on foo_abc_detail is just called id.
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "foo")
#Audited
#AuditOverride(forClass = Auditable.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class BaseFoo extends Auditable implements Serializable {
#Id
#Column(name = "id")
private Long id;
//other fields
}
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Audited
#AuditOverride(forClass = BaseFoo.class)
#DiscriminatorValue("2")
public class FooAbc extends BaseFoo implements Serializable {
#EqualsAndHashCode.Exclude
#ToString.Exclude
#NotAudited
#OneToOne(mappedBy = "fooAbc",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true,
optional = false)
private FooAbcDetail fooAbcDetail;
//other fields
}
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "id")
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#MapsId //also tried #MapsId("id")
#OneToOne(fetch = FetchType.LAZY)
private FooAbc fooAbc; //also tried BaseFoo
//other fields
}
Okey I solved. I don't know if this is the best way to do it but it works! I replaced the #MapsId annotation via #JoinColumn. Final result of the FooAbcDetail class like as below.
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "id")
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
private FooAbc fooAbc;
}
Used this as my base point: https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/
I have an entity named Attendance that has an and emebedid AttendacneId that has two columns lectureId (string) and studenId(UUID). The attendance entity also has other properties as well containing a ManyToOne relationship with student and lecture enity using the #MapsId on them. The student extends user entity where the id resides. now when I try to save anything I get this error.
java.util.NoSuchElementException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at org.hibernate.cfg.annotations.TableBinder.linkJoinColumnWithValueOverridingNameIfImplicit(TableBinder.java:724)
at org.hibernate.cfg.PkDrivenByDefaultMapsIdSecondPass.doSecondPass(PkDrivenByDefaultMapsIdSecondPass.java:37)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1684)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1641)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:286)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:473)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:84)
at utility.Utils.<clinit>(Utils.java:60)
at utility.Testing.main(Testing.java:21)
My AttendanceId class
#Embeddable
public class AttendanceId implements Serializable {
#Column(name = "lecture_fid")
#Getter
#Setter
private String lectureId;
#Column(name = "student_fid", columnDefinition = "uuid")
#Getter
#Setter
private UUID studentId;
public AttendanceId() {}
}
My Attendance class
#Entity
#Table(name = "attendance")
public class Attendance implements Serializable {
#EmbeddedId
#Getter
#Setter(AccessLevel.PRIVATE)
private AttendanceId id;
// other fields
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("lectureId")
private Lecture lecture;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("studentId")
private Student student;
}
My Student Class
#Entity
#PrimaryKeyJoinColumn(name = "id")
public class Student extends User implements Serializable,
Comparable<Student> {
// other fields
#OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
#Getter
private List<Attendance> attendances = new ArrayList<>();
}
My User class
#Entity
#Table(name = "users", uniqueConstraints = {
#UniqueConstraint(columnNames = {"email", "username"})})
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class User implements Serializable {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
#Column(columnDefinition = "UUID")
#Getter(AccessLevel.PUBLIC)
#Setter(AccessLevel.PUBLIC)
private UUID id;
// other fields.
}
My Lecture Class
#NamedNativeQueries(
#NamedNativeQuery(name = "getLectureId", query = "select
get_lecture_id()")
)
#Entity
#Table(name = "lecture")
public class Lecture implements Serializable {
#Id
#Column(name = "id")
#GenericGenerator(name = "lectureIdGenerator",
strategy = "entities.LectureIdGenerator")
#GeneratedValue(generator = "lectureIdGenerator")
#Getter
#Setter
private String id;
#OneToMany(mappedBy = "lecture", fetch = FetchType.LAZY)
#Getter
private List<Attendance> attendances = new ArrayList<>();
// other fields
}
I think the inheritance is causing an issue. There are other classes/entities as well that extends the user entity. If anyone could help me find the problem that will be great. Thank you.
This issue is caused by the HHH-7135 Hibernate issue.