I have a table User with columns
user_id, first_name,last_name, created_time(timestamp)
I have a class User Entity
#Getter
#Setter
#Table(name="user")
#Entity
public class User {
#Id
private Long userId;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="created_time")
private Timestamp createdTime;
}
I have an interface User Repository
public interface UserRepository extends CRUDRepository<User,Long> {
User findByUserId(Long id);
}
The created_time stored in database table is 2020-09-08 15:38:13 and when i read the object using spring data jpa it returned as "2020-09-08 21:08:13"
How should i ensure that this automatic time zone conversion not to happen?
The root cause of the problem is that Jackson automatically converts the timestamp values to UTC and then serializes the same.
In order to correct this behavior, you can add following property to your application.properties and specify the same timezone value as is being used by your DB server.
spring.jackson.time-zone=Asia/Kolkata
There is an article that explains this problem and also proposes solutions.
You can also have a look at this answer.
Related
I have a strange situation with Spring Boot 2.7.7 and need some guidance how to troubleshoot.
Here is an Account entity and for security reasons I hide the password field.
#Entity
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "accounts")
public class Account extends BaseEntity implements Serializable {
...
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Column(name = "password")
private String password;
}
On the frontend side (ReactJS) I don't see the password field, so the above annotation works.
After I try to change an attribute value of the account on the frontend side, e.g. title, the password is reset to null.
Here is the update service:
#Autowired
private IAccountRepository accountRepository;
public Account updateAccount(Long id, Account account) throws CustomException {
return accountRepository.save(account);
}
Why is this happening? And how could I ensure that the password is not set to null after each update of the account?
The password is hidden to your frontend. If the frontend sends a request to the backend the password field is not given. There is an easy way to fix it. Before you save the updated entity to your DB, make a request to your Database to retrieve the full object (The best would be using the ID of the Entity). Update that object with the fields which changed, the password field is given there. Save that updated object to your Database where everything is given.
I have a scenario where I have the user table and the address table. The address table is a value objects in domain driven design in my understanding. How do I store value objects in mysql database? this sounds a bit confusing but I couldn't understand this idea value objects are immutable but how to store them?
Below are classes of my two entity
user.java
#Getter #Setter #NoArgsConstructor
#Entity // This tells Hibernate to make a table out of this class
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JsonProperty("userid")
#Column(name="userid")
private Long user_id;
#JsonProperty("user_nome")
private String nome;
#JsonProperty("user_email")
#Column(unique = true, nullable = false)
private String email;
#JsonProperty("user_cpf")
private String cpf;
#JsonProperty("user_telefone")
private String telefone;
#JsonProperty("user_celular")
private String celular;
#JsonProperty("user_senha")
private String senha;
#Column(name="createdAt", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#JsonProperty("user_createdAt")
private Date createdAt;
#Column(name="updateAt", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#JsonProperty("user_updateAt")
private Date updateAt;
/*Person p1 = new Person("Tom", "Smith");
p1.setId(1L);
p1.setStartDate(new Date(System.currentTimeMillis())); */
}
class Address:
#Getter #Setter #NoArgsConstructor
#Entity // This tells Hibernate to make a table out of this class
#Table(name="address")
public class Address {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JsonProperty("address_id")
private Long address_id;
#JsonProperty("address_endereco")
private String endereco;
#JsonProperty("address_bairro")
private String bairro;
#JsonProperty("address_numero")
private String numero;
#JsonProperty("address_complemento")
private String complemento;
#JsonProperty("address_cidade")
private String cidade;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "userid")
private User userid;
}
Basically however you want: you could enforce immutability in the database, but you don't have to. Immutability can be enforced in the database by creating an unique constraint on a combination of values of an address, zipcode + house number for example.
As a database administrator I personally don't like developers enforcing immutability in the database because I see implementing/enforcing business logic in the database as a category error. What is an immutable value within the domain, to me is just data that needs to be consistently stored. Database rules are meant to ensure data consistency and the added complexity of implementing immutability in the database can interfere with that. Lets do a thought experiment:
You ensure that an address value is unique in the database with a constraint that covers all properties and store your data. Some time later a customer places an order that happens to have the same address, but he lives on the North Pole. The order is saved but the address isn't because my server throws an error because the address violates the constraint because it already exsists in the US, but that's not saved/part of the constraint. Now I have a problem because that orphaned order violates the data model, you complain to me because my server threw an error and now it's up to me to figure out what's wrong with your design decision to apply the abstract concept of immutability outside your domain, and have to update the data definition in a production environment in order to fix it.
So I think it's best you acknowledge that by storing data it leaves your domain and that is a risk your design should take into account. What I'd advice (or silently implement haha) would be the addition of an ID within the table and a record versions of the same 'immutable value' for tracability, consistency and agility to react to unforseen circumstances. Just like with user and transaction entities ;)
I'm trying to build a simple application with Quarkus. Currently, I have two entity classes, which are related one-to-many:
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birthdate;
#OneToMany(mappedBy = "person")
public List<Address> addresses;
public static Person findByNameFirst(String name) {
return find("name", name).firstResult();
}
}
#Entity
public class Address extends PanacheEntity {
public String street;
...etc...
#ManyToOne
public Person person;
}
These are used by a simple REST webservice, which should store a Person to the database, select it again an return it:
#GET
#Path("storePerson")
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Person storePerson(
#QueryParam("name")String name,
#QueryParam("birthdate")String birthdate)
{
LocalDate birth = LocalDate.parse(birthdate, DateTimeFormatter.BASIC_ISO_DATE);
Person person = new Person(name, birth);
person.persistAndFlush();
Person p2 = Person.findByNameFirst(name);
return p2;
}
When calling the webservice the first time, the result is a JSON object with the stored data, which is as expected. When called again, an internal server error is thrown:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [Person.addresses] - no session and settings disallow loading outside the Session
As I understand, the error is thrown because the transaction only lasts until the storePerson method ends, but the conversion to JSON is happening outside of the method.
How can I prevent this error? I have read about the hibernate parameter "enable_lazy_load_no_trans" but it seems it is not supported in Quakus' application.properties.
The idea is to use a mapper framework such as MapStruct.
We don't recommend to directly expose your entities for 2 reasons:
the issue you have,
API management in the long run: you might have to change your model and not your API or the opposite.
There is an example here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-quarkus .
The Quarkus version used is a bit old but AFAICS it should still work with latest Quarkus.
You can make the error go away by using Hibernate.initialize(person.addresses), then the collection gets initialized before the transaction ends.
I'm currently trying to setup a database – using Java only. Given this simple class that might appear in the average social network app:
#Entity
class User {
#Id
private String email;
private String name;
private String otherInfo;
#ManyToMany
private List<User> contacts;
}
When the user logs in, he should receive the basic information and the list of contacts with their basic info, but not their contacts. To reduce the amount of boiler-plate code, I want to use a standard solution like Gson. However, even with lazy fetch the whole user is loaded on gson.toJson(user).
Therefore I thought of extracting the basic infos into a base class BasicUser and changing the contacts to List<BasicUser>. Now I only need to somehow circumwent the discriminator column when I fetch the contacts – of course they are all saved as complete users on the server. Unfortunately, I don't know how to archieve that. Any ideas?
If you need to get only part of the entity you can use projections. In your case it can be, for example, like this:
public interface BaseUser {
String getEmail();
String getName();
String getOtherInfo();
}
public interface UserRepo extends JpaRepository <User, String> {
List<BaseUser> findAllBy();
}
Using Jackson for serialization, the problem can be solved without writing custom serialization code. BasicUser contains the getters of the attributes, I want to serialize:
public interface BasicUser {
String getEmail();
String getFirstName();
String getLastName();
}
With a single annotation the contacts attribute is interpreted as a list of BasicUsers:
#Entity
public class User implements BasicUser {
#Id
private String email;
private String firstName;
private String lastName;
#ManyToMany
#JsonSerialize(contentAs = BasicUser.class)
private List<User> contacts = new ArrayList<>();
// ... implemented getters
}
You shouldn't have to modify your domain model just to accomodate a serialization library.
If you only want certain fields of a collection to be exposed to JSON, you could use Jackson with #JsonView (see here: How to serialize using #Jsonview with nested objects) not sure if Gson provides a similar feature as I have never used it extensively.
I am very new in Hibernate. I am using Hibernate with JPA. I have an annotated entity class and a table related to that entity class.
#Entity
public class Test implements Serializable {
#Id
#GenericGenerator(name="inc" , strategy="identity")
#GeneratedValue(generator="inc")
private int id;
private String address; // setter getter and constructor
}
When saving this entity, it insert the data into the db. But during application running process another application is inserting data into same table. when my application try to save the data then Duplicate entry '59' for key 'PRIMARY' exception generated. So I want to use a generator which can insert the data and generate id in database level rather than application level and the identifier must saved back to my entity.
Use the Table generator strategy or the sequence generator.
You do not have to specify a generator. You can use the default generator and never set the id manually. If the error still comes post your merge/persist method.
More informations about generators can you found here https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing
#Entity
public class Test implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String address; // setter getter and constructor
}