I'm using single table inheritance and trying to fetch entities by their discriminator column but I'm getting errors ...
I don't have the discriminator value as a field in the class.
Here's my code :
User Class
#Entity
#Table(name = "MT_User")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "userType", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("CLASSIC")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int uid;
public String imageFileName;
private String pseudo;
private String email;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "MT_User_Playlist", joinColumns = #JoinColumn(name = "uid"), inverseJoinColumns = #JoinColumn(name = "tlid"))
private List<Playlist> playlists;
...
}
Admin Class
#Entity
#DiscriminatorValue("ADMIN")
public class Admin extends User implements SuperUser {
//no fields
...
}
User Repository
public interface UserRepository extends CrudRepository<User, Integer> {
User findByPseudo(String pseudo);
User findByEmail(String email);
void deleteByPseudo(String pseudo);
void deleteByEmail(String email);
List<User> findByUserType(String userType);
#Query("from mt_user where user_type=ADMIN")
List<User> findAdmins();
#Query("from mt_user where user_type=ADMIN and pseudo=?1")
User findAdminByPseudo(String pseudo);
}
Here are the errors I get :
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: mt_user is not mapped [from mt_user where user_type=ADMIN and pseudo=?1]
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: mt_user is not mapped [from mt_user where user_type=ADMIN]
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List org.ThePouil.projects.mytunes.DAO.UserRepository.findByUserType(java.lang.String)! No property userType found for type User!
Thanks for your help !
:: EDIT ::
This solved my problems :
#Query("from User")
List<User> findUsers();
#Query("from Admin")
List<User> findAdmins();
#Query("from Admin where pseudo=?1")
User findAdminByPseudo(String pseudo);
You should use class name of your #Entity and not tables names in your queries. It should be from Admin or from User.
Related
I am using Spring Boot 2.2.2 . I have faced an unusual issue that EntityGraphType.FETCH did not work whenever I fetch User entity.
It fetches all the FetchType.EAGER (Company, UserFile) entities regardless the EntityGraph type. I tried with attributePaths in repository, it has the same issue.
#Entity
#NamedEntityGraph(name = "User.noAssociation")
#NamedEntityGraph(name = "User.profilePic", attributeNodes = {#NamedAttributeNode("profilePic")})
#Getter
#Setter
public class User extends TimeAudit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Email
private String email;
#ManyToOne(fetch = FetchType.EAGER)
private Company company;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private UserFile profilePic;
}
and
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#EntityGraph(value = "User.profilePic", type = EntityGraphType.FETCH)
#Query("SELECT u FROM User u WHERE u.email="email")
User findByEmail(String email);
}
Change
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#EntityGraph(value = "User.profilePic", type = EntityGraphType.FETCH)
#Query("SELECT u FROM User u WHERE u.email="email")
User findByEmail(String email);
}
to
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#EntityGraph(value = "User.profilePic", type = EntityGraphType.LOAD)
#Query("SELECT u FROM User u WHERE u.email="email")
User findByEmail(String email);
}
I have two entities SuperAlbumEntity and AlbumEntity reflecting the same table "albums".
SuperAlbumEntity:
#Entity
#Table(name = "albums")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class SuperAlbumEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
//other fields
}
AlbumEntity:
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "albums")
public class AlbumEntity extends SuperEntity{
//some fields
#Column(name = "country")
private String country;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "country_name", referencedColumnName = "country")
private Set<CountryEntity> countrySet = new HashSet<>();
}
AlbumEntity has #OneToMany mapping to CountryEntity:
#Entity
#Table(name = "countries")
public class CountryEntity implements Serializable {
#Id
String id;
String country_name;
//other fields
}
Running my application I get the folowing error:
...
Caused by: org.hibernate.AnnotationException: referencedColumnNames(country) of CountryEntity.countrySet referencing AlbumEntity not mapped to a single property
...
What's interesting is that if I move country field from SuperAlbumEntity to AlbumEntity everything just works fine...
Can someone explain me why I get this error?
I'm not sure but I think is connected with the type of inherence that you used it. Try to modify your superclass to something like this:
SuperAlbumEntity:
#MappedSuperclass
public abstract class SuperAlbumEntity {
}
AlbumEntity:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name = "albums")
public class AlbumEntity extends SuperEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "country_name", referencedColumnName = "country")
private Set<CountryEntity> countrySet = new HashSet<>();
}
I have no locking defined and I can't figure out why I get this error when updating an attribute of User entity:
Object of class [com.****.User] with identifier [2df1fe02-e679-4c5e-bc9c-c023e5be1460]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.****.User#2df1fe02-e679-4c5e-bc9c-c023e5be1460]
Disconnected from the target VM, address: '127.0.0.1:55712', transport: 'socket'
It happens just when calling save on the user instance:
User user = userRepository.findByUserName(username);
user.setMonthlyThreshold(monthlyThreshold);
userRepository.save(user);
User repository is defined as follows:
#Repository
public interface UserRepository extends JpaRepository<User, String> {
User findByUserName(String username);
}
User class:
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private UUID id;
#NotBlank
#ApiModelProperty(notes = USER_USERNAME)
private String userName;
...// other attributes
What am I missing?
I'm using MySQL DB, Spring Boot, java 8.
The solution that worked for me was extracted from this blog article and looks like this:
public class User {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
private UUID id;
...
I have user entity:
#ToString
#Data
#Entity
#Table(name = "users")
#NamedEntityGraph(name = "UserWithItems",
attributeNodes = {
#NamedAttributeNode("items"),
#NamedAttributeNode("roles")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Item> items;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Role> roles;
}
item:
#ToString(exclude = "user")
#Data
#Entity
#Table(name = "items")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
private User user;
}
role:
#ToString
#Data
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
private User user;
}
I want load user with items and roles. I use #NamedEntityGraph. It is my repository:
#EntityGraph(value = "UserWithItems", type = EntityGraph.EntityGraphType.LOAD)
#Query("select u from User u where u.id = ?1 and u.name =?2")
User getOneById(Long id, String name);
But I get an error:
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.example.egerload.entity.User.roles, com.example.egerload.entity.User.items]
at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:75) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:108) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:212) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:143) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:119) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:85) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.query.internal.AbstractProducedQuery.makeQueryParametersForExecution(AbstractProducedQuery.java:1350) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1539) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1505) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
... 41 common frames omitted
You can split your "UserWithItems" #NamedEntityGraph into two #NamedEntityGraphs, resulting in two queries, as described in Hibernate throws MultipleBagFetchException - cannot simultaneously fetch multiple bags - answer of Vlad Mihalcea.
User
#ToString
#Data
#Entity
#Table(name = "users")
#NamedEntityGraphs(
{
#NamedEntityGraph(
name = "UserWithItems",
attributeNodes = {
#NamedAttributeNode("items")
}
),
#NamedEntityGraph(
name = "UserWithRoles",
attributeNodes = {
#NamedAttributeNode("roles")
}
),
}
)
public class User {
...
}
I assume you have a repository class. For example with extends JpaRepository. Use each NamedEntityGraph on an extra method. (I have omitted the name condition and #Query("..."). The id condition should be sufficient, since it is the user's identifier. #Query("...") is not needed.)
UserRepository
public interface UserRepository extends JpaRepository<User, Long> {
#EntityGraph(value = "UserWithItems", type = EntityGraph.EntityGraphType.LOAD)
Optional<User> getOneWithItemsById(Long id);
#EntityGraph(value = "UserWithRoles", type = EntityGraph.EntityGraphType.LOAD)
Optional<User> getOneWithRolesById(Long id);
....
}
Finally, you can call both methods in a service.
UserService
public interface UserService {
Optional<User> readById(Long id);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
#Transactional
public Optional<User> readById(Long id) {
// Load user with items into persistence contex
userRepository.getOneWithItemsById(id);
// Load user with roles into persistence context
// (There is only one user instance by id within the persistence context)
return userRepository.getOneWithRolesById(id);
}
}
model classes
#Entity
#Table(name="RequisitionRequest")
public class Requisition {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String requestedPerson;
private String contactDetails;
private String appliedDate;
private String branch;
private String department;
private String status;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "RequisitionRequest")
private List<Nationality> nationality;
//getters and setters
}
class Nationality
#Entity
#Table(name="nationality")
public class Nationality {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String nationality;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "nationality_id", nullable = false)
private Requisition requisition;
//getters and setters
}
repository classes
#Repository
public interface NationalityRepository extends JpaRepository<Nationality,Long>{
}
#Repository
public interface RequisitionRepository extends JpaRepository<Requisition,Long> {
}
service class
#Service
public class RequisitionServiceImpl implements RequisitionService {
#Autowired
RequisitionRepository requisitionRepository;
#Override
public void save(Requisition requisition) {
requisitionRepository.save(requisition);
}
}
controller class
#RestController
public class RequisitionController {
#Autowired
RequisitionService requisitionService;
#Autowired
RequisitionRepository requisitionRepository;
#PostMapping("/requisition/")
#CacheEvict(value = "requisitions", allEntries=true)
public ResponseEntity<Requisition> addRequisition(#RequestBody Requisition requisition) {
System.out.print(requisition);
Requisition requisitionR = new Requisition();
Requisition response = new Requisition();
requisitionR.setBranch(requisition.getBranch());
requisitionR.setDepartment(requisition.getDepartment());
requisitionR.setExpectedDateofJoin(requisition.getExpectedDateofJoin());
//requisitionR.setNationality(requisition.getNationality());
requisitionRepository.save(requisitionR);
return new ResponseEntity<Requisition>(response, HttpStatus.OK);
}
i am learning spring boot,just writing sample program to learn spring boot+jpa+one-to-many relation.when execute this above code getting error like
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: officekitRecruitment.model.Nationality.RequisitionRequest in officekitRecruitment.model.Requisition.nationality
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:769) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:719) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1623) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:278) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
I tried the solutions already mentionned in this site but it doesn't work for me.
Can anybody helps me to resolve this issue
You should refer to the name of the Field in mapped entity on your MappedBy parameter.
Like that
mappedBy = "requisition"