I tried to deploy my application on AWS and Openshift, the applications seems to work fine until I try to access GET /users endpoint after a POST (saved user without company data).
It returns:
There was an unexpected error (type=Internal Server Error, status=500).
could not prepare statement; SQL [select companies0_.users_id as users_id1_8_0_, companies0_.companies_id as companie2_8_0_, company1_.id as id1_0_1_, company1_.contact_id as contact_4_0_1_, company1_.gallery_id as gallery_5_0_1_, company1_.active as active2_0_1_, company1_.promoted as promoted3_0_1_, company1_.location_id as location6_0_1_, company1_.profile_id as profile_7_0_1_, company1_.subscription_id as subscrip8_0_1_, contact2_.id as id1_1_2_, contact2_.email as email2_1_2_, contact2_.phone as phone3_1_2_, gallery3_.id as id1_2_3_, location4_.id as id1_3_4_, location4_.address as address2_3_4_, location4_.city as city3_3_4_, location4_.country as country4_3_4_, location4_.country_code as country_5_3_4_, location4_.latitude as latitude6_3_4_, location4_.longitude as longitud7_3_4_, profile5_.id as id1_5_5_, profile5_.company_category as company_2_5_5_, profile5_.creation_date as creation3_5_5_, profile5_.description as descript4_5_5_, profile5_.name as name5_5_5_, profile5_.thumbnail as thumbnai6_5_5_, subscripti6_.id as id1_6_6_, subscripti6_.subscription_end_date as subscrip2_6_6_, subscripti6_.subscription_start_date as subscrip3_6_6_ from users_companies companies0_ inner join companies company1_ on companies0_.companies_id=company1_.id left outer join contacts contact2_ on company1_.contact_id=contact2_.id left outer join gallery gallery3_ on company1_.gallery_id=gallery3_.id left outer join locations location4_ on company1_.location_id=location4_.id left outer join profiles profile5_ on company1_.profile_id=profile5_.id left outer join subscription subscripti6_ on company1_.subscription_id=subscripti6_.id where companies0_.users_id=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
The logs are as follows:
org.springframework.dao.InvalidDataAccessResourceUsageException:could not prepare statement
Caused by: java.sql.SQLSyntaxErrorException:user lacks privilege or object not found:COMPANIES in statement
Caused by: org.hsqldb.HsqlException:user lacks privilege or object not found:COMPANIES
But all these works perfectly fine on local server with mysql.
The controller:
#RestController
public class UserController {
#Autowired
UserRepository repository;
#GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity getUsers() {
return new ResponseEntity<>(repository.findAll(), HttpStatus.OK);
}
}
Repository:
#Repository
public interface UserRepository extends JpaRepository<User, String> {
}
User model:
#Entity
#Table(name = "users")
#DynamicUpdate
#EntityListeners(AuditingEntityListener.class)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false)
#SerializedName("id")
private long id;
#Column(name = "name")
#SerializedName("name")
private String name;
#Column(name = "email", unique = true, updatable = false)
#SerializedName("email")
private String email;
#Column(name = "password")
#SerializedName("password")
#JsonIgnore
private String password;
#Column(name = "accessToken")
#SerializedName("accessToken")
private String accessToken;
#Column(name = "phoneNumber")
#SerializedName("phoneNumber")
private String phoneNumber;
#Column(name = "createdAt", nullable = false, updatable = false)
#SerializedName("createdAt")
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date createdAt;
#Column(name = "updatedAt", nullable = false)
#SerializedName("updatedAt")
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private Date updatedAt;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Company> companies;
//getters & setters etc
}
Company model:
#Entity
#Table(name = "companies")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false)
#SerializedName("id")
private long id;
#OneToOne(cascade = CascadeType.ALL)
#SerializedName("profile")
private Profile profile;
#OneToOne(cascade = CascadeType.ALL)
#SerializedName("contact")
private Contact contact;
#OneToOne(cascade = CascadeType.ALL)
#SerializedName("location")
private Location location;
#OneToOne(cascade = CascadeType.ALL)
#SerializedName("gallery")
private Gallery gallery;
#OneToOne(cascade = CascadeType.ALL)
#SerializedName("subscription")
private Subscription subscription;
#Column(name = "active", columnDefinition = "tinyint(1) default 0")
#SerializedName("active")
private boolean isActive;
#Column(name = "promoted", columnDefinition = "tinyint(1) default 0")
#SerializedName("promoted")
private boolean isPromoted;
//getters & setters etc
}
Application properties:
spring.datasource.url=jdbc:mysql://host:3306/database
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
Any idea what cause this problem? Also I mentioned above this works perfectly fine on local machine.
UPDATE Its seems if I don't provide data for every object in relations when saving (exp.User/Company,Company/Profile,Company/Contact etc) a user to db it gives this error.
Related
I have an issue in testing save operation by Jpa.
I have a User object:
#Entity
#Table(name = "USERS", schema = "ecommercy")
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public class User {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(updatable = false, nullable = false, columnDefinition = "VARCHAR(255)")
private String id;
#Column(unique = true, nullable = false)
private String email;
#Column(unique = true)
private String phoneNumber;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String name;
private String lastName;
#OneToMany(mappedBy = "user")
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private List<Address> addresses;
#OneToOne(mappedBy = "user")
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private Basket basket;
#OneToMany(mappedBy = "user")
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private List<Order> orders;
#Builder.Default
#Column(nullable = false)
private int points = 0;
#OneToMany(mappedBy = "user")
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private List<Comment> comments;
#Builder.Default
#Convert(converter = StringListConverter.class)
#Column(updatable = false)
private List<String> roles = List.of("USER");
#Builder.Default
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
#Column(nullable = false, updatable = false)
private Date createdAt = new Date();
#Builder.Default
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private Date updatedAt = new Date();
}
As you can see some of the user fields such as name or email have #Column(unique = true) or #Column(nullable = false)
When I run the application everything is okay and works perfectly.
But when I try to test these two annotations by Junit5 nothing will happen and no exception will be thrown and tests will be passed succesfully.
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserRepositoryTests {
#Autowired
private UserRepository userRepository;
#Test
void ifEmailIsNullMustThrowException() {
// Arrange
User user = User.builder()//
.name("John")//
.lastName("Doe")//
.password("password")//
.phoneNumber("123456789")//
.build();
// Act
User savedUser = userRepository.save(user);
// Assert
assertNull(savedUser);
}
#Test
void ifAUserExistWithGivenEmailMustThrowException() {
// Arrange
User user1 = User.builder()//
.name("John")//
.lastName("Doe")//
.email("johndoe#email.com")//
.password("password")//
.phoneNumber("123456789")//
.build();
User user2 = User.builder()//
.name("John")//
.lastName("Doe")//
.email("johndoe#email.com")//
.password("password")//
.phoneNumber("123456789")//
.build();
userRepository.save(user1);
// Act
User savedUser = userRepository.save(user2);
// Assert
assertNull(savedUser);
}
}
The #DataJpaTest makes your test #Transactional which means everything in the test runs in a transaction and will rollback after the test. That combined with the default flush mode of AUTO (which will flush automatically on commit) makes it that no actual SQL will be issues to the database in your test. (Enabling query logging would show this). With no SQL to the database no constraints will trigger and thus no exception.
To fix you can do 2 things.
Use saveAndFlush instead of save, as that will flush changes to the database and thus issue SQL.
Inject the TestEntityManager and after all your calls, before the asserts, call the flush method so synchronize the changes to the database and issue the SQL.
Either will work as it will trigger the flushing of the changes.
I have created JPA entities class. But using basic query taking humongous amount of time for basic sql query having 100 records in database.
I have 4 tables. (Group/GroupA/GroupB/GroupC) having below structure.
#Table(name = "group")
public class Group implements Serializable {
#Id
#Column(name = "id")
private String id;
#Column(name = "publish_date")
private LocalDate publishDate;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupA groupA;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupB groupB;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupC groupC;
}
#Table(name = "groupA")
public class GroupA {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
#Table(name = "groupB")
public class GroupB {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
#Table(name = "groupC")
public class GroupC {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
Repository class:
#Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
#Query("select r from Group r WHERE r.publishDate = ?1")
List<Group> findAllGroupForDate(LocalDate businessDate);
}
The above basic query is taking 1 minute 30 seconds for around 100 rows in Main Group table.
Please help if I am making anything wrong in entity class.
THanks.
Try to use the Lazy loading concept.
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
i am currently building an Application with Spring and i have a Question there:
I want to have an Entity Address which looks like this:
#Entity(name = "Address")
#Table(name = "address")
#EntityListeners(AuditingEntityListener.class)
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
#Column(name = "postalcode")
private String postalCode;
#Column(name = "state")
private String state;
#Column(name = "street")
private String street;
public Address() {
}
}
I want to use this Address Entity in multiple Entities, for example in the User or Order Entity. Later, i will like to have many Entities which need an Address. But i don't want to specify each Relation in the Address Entity, otherwise it will get to complex. Is it possible to have a Link from the User to the Address with only specifying this Link in the User Entity?
My User Entity looks something like this:
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
mappedBy = "address",
orphanRemoval = true,
cascade = {
CascadeType.PERSIST,
CascadeType.REMOVE
}
)
private Address billingAddress;
public User() {
}
}
Yes, it is possible, but you don't actually need mappedBy property, otherwise you are telling JPA to search for a address property in the other side of the relationship (that you actually want to be unidirectional):
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
private Address billingAddress;
public User() {
}
}
You can read more about this in the following online resources:
https://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html
https://www.baeldung.com/jpa-one-to-one
https://javabydeveloper.com/one-one-unidirectional-association/
I would be very grateful to you for help.
I use Spring Boot 2.5.2.
DB: H2 with Liquibase
I need to change ticket History when attachment is removed.
This is my entities:
Ticket:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Ticket {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_owner_id", nullable = false)
private User owner;
#OneToMany(mappedBy = "ticket", orphanRemoval = true)
private List<Attachment> attachments;
#OneToMany(mappedBy = "ticket", orphanRemoval = true)
private List<History> history;
// other fields and relationships
}
User:
#Entity
#Table(name = "users")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "owner", orphanRemoval = true)
private List<Ticket> ownerTickets;
// other fields and relationships
}
Attachment:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String filename;
#Column(columnDefinition = "bytea")
private byte[] file;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ticket_id", nullable = false)
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
History:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class History {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#CreationTimestamp
#Column(name = "changed_date", nullable = false, updatable = false)
private LocalDateTime changedDate;
private String action;
private String description;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ticket_id")
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
Services:
JpaAttachmentService:
#Service
#AllArgsConstructor
public class JpaAttachmentService implements AttachmentService {
private final AttachmentRepository attachmentRepository;
private final HistoryService historyService;
#Transactional
#Override
public void delete(Long id, Long ticketId, Long userId) {
var attachment = attachmentRepository.getByIdAndTicketIdAndTicketOwnerId(id, ticketId, userId);
var action = "File is removed";
var description = "File is removed: " + attachment.getFilename();
var ticket = attachment.getTicket();
var user = attachment.getUser();
var history = new History(null, LocalDateTime.now(), action, description, ticket, user);
historyService.save(history);
attachmentRepository.deleteByIdAndTicketIdAndTicketOwnerId(id, ticketId, userId);
}
}
When I try to 'historyService.save(history)' I catch:
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "USER_ID"; SQL statement:
update history set action=?, description=?, ticket_id=?, user_id=? where id=? [23502-200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:459)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
at org.h2.message.DbException.get(DbException.java:205)
at org.h2.message.DbException.get(DbException.java:181)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:374)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:845)
at org.h2.command.dml.Update.update(Update.java:176)
at org.h2.command.CommandContainer.update(CommandContainer.java:198)
at org.h2.command.Command.executeUpdate(Command.java:251)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:191)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:152)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
... 144 more
This is 'var user' debug:
picture
I can't understand why I get this Hibernate Interceptor but not entity. I confused when I see inside interceptor required UserID and when I see 'null' outside of it. Could you help me with this problem, please?
Thanks in advance for any help.
The exception message is quite informative regarding the error you are experiencing. Take a look at the following part:
NULL not allowed for column "USER_ID"; SQL statement: update history
set action=?, description=?, ticket_id=?, user_id=? where id=?
What you attempt to do is to save an instance of a History entity which without passing in a reference to a User object. Since your relation dictates that the user reference cannot be null:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
To fix this, either make sure that a non-null reference to a User object is passed in when inserting/updating the History reference, or modify your database constraint design to allow for null user references at the History entity.
My problem was related to test, but in the further development I faced it again. So I want to share my solution.
Maybe it will help you.
Pay attention to CascadeType! Cascading operations must be specified above the link to the child relation. In my case, I have to remove this relation or leave Ticket with CascadeType.PERSIST
Fixed class Attachment:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String filename;
#Column(columnDefinition = "bytea")
private byte[] file;
#ManyToOne(fetch = FetchType.LAZY) // or cascade = CascadeType.PERSIST
#JoinColumn(name = "ticket_id", nullable = false)
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
I have a SQL query like this:
"Select UIProfileID from UserTable where UPPER(UserID) = UPPER('?1')".
I want to convert it to Spring JPA.
I want to write getUIProfileId() and return Integer. But I don't know how to implement. Because User table doesn't have UIProfileId column that it was joined from UIProfileTable table. Please help me solve it.
Currently, I have tables:
User.java
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(name = "UserTable")
public class User {
#Column(name = "UserID", length = 32, nullable = false)
#Id
private String name;
#ManyToOne
#JoinColumn(name = "DomainID", nullable = false)
private Domain domain;
#Column(name = "Password", length = 32, nullable = false)
private String password;
#ManyToOne
#JoinColumn(name = "UIProfileID", nullable = false)
private UIProfile uiProfile;
#Column(name = "ResPerpage", nullable = false)
private Integer resperpage;
#Column(name = "DefaultTab")
private Integer defaulttab;
#ManyToOne
#JoinColumn(name = "AdminProfile")
private AdminProfiles adminProfile;
#Column(name = "LanguageId")
private Integer languageId;
}
UIProfile.java
#Entity
#Getter
#Setter
#Table(name = "UIProfileTable")
public class UIProfile implements Serializable {
#Id
#Column(name = "UIProfileID", length = 11, nullable = false)
private Integer id;
#Column(name = "UIProfileName", length = 32, nullable = false)
private String name;
#OneToMany(mappedBy = "id.uiProfile")
private List<UIProfileTopLevel> topLevels;
}
UserRepository.java
public interface UserRepository extends Repository<User, String> {
Optional<User> findOne(String name);
#Query("Select UIProfileID from User where UPPER(UserID) = UPPER('admin')")
Integer getUIProfileId();
}
You can try this:
#Query("SELECT u.uiProfile.id from User u where UPPER(u.name)=UPPER('admin')")
Integer getUIProfileId();
Here User is the domain class name and u is the reference of User. with u we will access User's field NOT the column name which are specified with #Column or #JoinColumn Ex : #JoinColumn(name = "UIProfileID", nullable = false).