Can't use String as #Id with SpringData - mysql

I want to create new table on my database using this class
#Entity
#Table(name = "currency_rate")
public class CurrencyRate {
#Id
private String id;
#Column(name = "source_currency")
private String sourceCurrency;
#Column(name = "target_currency")
private String targetCurrency;
#Column(name = "exchange_rate")
private double exchangeRate;
#Column
private Date date;
#PrePersist
public void generateID() {
this.id = this.date.toString().replace("-", "") + sourceCurrency + targetCurrency;
}
//getters, setters
}
When I try to run my application with property
spring.jpa.hibernate.ddl-auto=create
I got this exception
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Specified key was too long; max key length is 1000 bytes
Looks like I can't use Spring as my ID? Changing type to Long solves problem, but I really wanted to go with String with this one. From what I searched, it should be totally doable.

As per tutorial from https://www.tutorialspoint.com/hibernate/hibernate_annotations.htm, the attribute can be defined by Column annotation in details.
#Column Annotation The #Column annotation is used to specify the
details of the column to which a field or property will be mapped. You
can use column annotation with the following most commonly used
attributes −
name attribute permits the name of the column to be explicitly
specified.
length attribute permits the size of the column used to map a value
particularly for a String value.
nullable attribute permits the column to be marked NOT NULL when the
schema is generated.
unique attribute permits the column to be marked as containing only
unique values.
What matters here for your question is length parameter, maybe you can try annotate your id like below:
#Id
#Column(length = 100)
private String id;

Related

How to store value objects in relational database like mysql

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 ;)

How to limit length of Varchar between 2 and 100

I have a table in MySQL database with a column named "name". I want that whenever any query comes to store name in this column, name's length should be >2 and <= 100.
And the name should start with alphabet only.
I don't want to place this check on Database Table. I want to place this check inside code.
#Entity
#Table(name = "member")
public class Member implements Serializable{
private static final long serialVersionUID = 9045098179799205444L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Column(name = "name")
String name;
#Column(name = "email")
String email;
}
Validate it before the persistence with javax.validation.constraints.Size;
#Size(min = 2, max = 200, message
= "name field must be between 2and 200 characters")
#Column(name = "name")
String name;
There are Bean Validation annotations, like #Size and #Pattern for this purpose. You can annotate the desired field with them. Here is a list of those annotations.
I don't want to place this check on Database Table. I want to place this check inside code.
EDIT: This post is for people who don't want to use annotations and if you consider the Entity as 'Datatable'. If you want to use annotations then use the regex below with the #Pattern annotation
Well, if you don't want to store this in the database you either need to handle this in the service layer above the POJO or inside the Model Member Class.
Depending on your perspective towards POJO/DTO with or without business logic.
The check itself would be as simple as
public void setName(String name){
Pattern pattern = Pattern.compile("^[A-Za-z].{1,100}");
Matcher matcher = pattern.matcher(test);
if(matcher.matches()){
// save here
}else{
// handle error here (exception?!)
}
}
This pattern checks, wheater the string starts with an alphabet character and has at least 2 more character after the alphabet character, up to a maximum of 99 characters after the first one.

JacksonMapping Exception :: HttpMessageNotWritableException: Could not write content: (was java.lang.NullPointerException)

Framework Spring MVC 4.x
Hibernate 4.x
Jackson 2.8
I have two columns one is publishDate and createdDate. Publish date user need to manually enter it. Created date in MySQL column has a default set as CURRENT_TIMESTAMP, so when an entry is created DB will automatically timestamp the entry.
I have a book POJO publishDate and createdDate field... publishDate can handle null data for some reason. But Timestamp field gets an exception. Why is this?
org.springframework.http.converter.HttpMessageNotWritableException: Could not
write content: (was java.lang.NullPointerException) (through reference chain:
java.util.HashMap["results"]->java.util.ArrayList[30]-
>com.app.books.Book["dateCreated"]); nested exception is
com.fasterxml.jackson.databind.JsonMappingException: (was
java.lang.NullPointerException) (through reference chain:
java.util.HashMap["results"]->java.util.ArrayList[30]-
>com.app.books.Book["dateCreated"])
I tried to suppress this by adding annotation, I tried several of them because I keep reading conflicting info on the comments section on which to use.
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) // but show JsonSerialize is deprecated
#JsonInclude(Include.NON_NULL)// Include can not be resolved as variable
#JsonInclude(JsonInclude.Include.NON_DEFAULT) // finally doesn't give me an error but I still get the same exception.
This is my book Class
#Entity
#Table(name="books")
#Component
public class Book implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2042607611480064259L;
#Id
#GeneratedValue
private int id;
#NotBlank
private String name;
#NotBlank
#Size(min=2, max=16)
private String ispn;
#DecimalMin(value = "0.1")
private double price;
//#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
//#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
//#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_DEFAULT)
private Timestamp dateCreated;
private Date datePublished;
Very wried... Thanks for helping..
You may have better results switching from primitives (int & double) to Java classes (Int & Double) in your Book class. See JsonMappingException (was java.lang.NullPointerException) also JSON: JsonMappingException while try to deserialize object with null values

How do I POST through RestTemplate a class with embedded members?

I have a class with a few members, and the associated setters and getters:
public class Tester implements Serializable {
#Column(name="ID", nullable=false, unique=true)
#Id
#GeneratedValue(generator="LOCATION_FACILITYTYPE_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name="LOCATION_FACILITYTYPE_ID_GENERATOR", strategy="native")
private int ID;
#Column(name="Value", nullable=false, unique=true, length=4)
private String value;
#Column(name="Name", nullable=false, unique=true, length=8)
private String name;
#ManyToOne(targetEntity=location.FacilityType.class, fetch=FetchType.LAZY)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="FacilityTypeID", referencedColumnName="ID", nullable=false) })
private location.FacilityType facility;
In a JUnit, I am trying to test creating a Tester element:
Tester trythis = new Tester();
trythis.setName("Herewe");
trythis.setValue("wow1");
Tester jnode = restTemplate.postForObject(TestBase.URL + "tester/", trythis, Tester.class);
This works as expected. However, if I use code like this to include an embedded member:
FacilityType ft = new FacilityType();
ft.setValue("AL");
ft.setName("2adamlec");
Tester trythis = new Tester();
trythis.setName("Herewe");
trythis.setValue("wow1");
trythis.setFacility(ft);
Tester jnode = restTemplate.postForObject(TestBase.URL + "tester/", trythis, Tester.class);
where the embedded member with value=AL does not yet appear in the database, I still get a new row created in the Tester table ... but the value and name columns in Tester are filled with the values (AL and 2adamlec) defined for FacilityType.
Note that we are using the JPARepository framework for FacilityType and Tester. The CRUD functions are thus handled 'under the covers', and I can't debug the POST processing. I wonder if this is associated with the fact that a GET for Tester data will only return the primitive fields in the JSON reply, since there is no projection defined for FacilityType.
Am I doing something wrong to cause the FacilityType fields to be saved in lieu of the desired Tester fields in the Tester table?
The short answer: when creating the item, you have to provide the data in the same JSON format that the server expects it. If you have an embedded class, you have to create a class where the facility member is a String to house a URL, then set that member to the URL corresponding to the existing instance of the embedded class. On the receiving end, you also need a new class like this:
public class FMT_Tester_RCV {
public FMT_Tester_RCV() { }
private String value;
private String name;
private Integer id;
private Integer ormid;
private JsonNode _links;
where you can travel down the JsonNode to get the link to the embedded class instance.

Can not set java.lang.Integer field id to org.hibernate.id.IdentifierGeneratorHelper

I need to store some data in a MySQL-database using Jpa 2/Hibernate 3.5.1. For legacy reasons the table I want to store data in has a compound primary key. The first part of the primary key is of type INTEGER (auto-increment-value), the second part is of type BIGINT (Long in Java-code - to be set manually before persisting).
I have implemented (example code below the stacktrace) the combined primary key via the #IdClass-Annotation, the first-key-part also has a generation strategy set: #GeneratedValue(strategy = GenerationType.IDENTITY)
When trying to persist an object with code like this
...
TestData testData = new TestData("data");
testData.setIdPartTwo(2L);
entityManager.getTransaction().begin();
entityManager.persist(testData);
entityManager.getTransaction().commit();
the following exception is thrown:
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of org.example.persistence.TestDataId.idPartOne
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1174)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:674)
at org.example.persistence.PersistenceTest.shouldPersistTestData(PersistenceTest.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:640)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:627)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:799)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1103)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:137)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:121)
at org.testng.TestRunner.runWorkers(TestRunner.java:1098)
at org.testng.TestRunner.privateRun(TestRunner.java:727)
at org.testng.TestRunner.run(TestRunner.java:581)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:315)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:310)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:272)
at org.testng.SuiteRunner.run(SuiteRunner.java:221)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:40)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:83)
at org.testng.internal.thread.ThreadUtil$CountDownLatchedRunnable.run(ThreadUtil.java:151)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of org.example.persistence.TestDataId.idPartOne
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:151)
at org.hibernate.mapping.Component$ValueGenerationPlan.execute(Component.java:438)
at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:122)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:800)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:774)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:778)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:668)
... 24 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Integer field org.example.persistence.TestDataId.idPartOne to org.hibernate.id.IdentifierGeneratorHelper$2
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
at java.lang.reflect.Field.set(Field.java:657)
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:139)
... 35 more
My entity-class looks like this:
#Entity
#IdClass(TestDataId.class)
public class TestData implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idPartOne;
#Id
private Long idPartTwo;
private String data;
public TestData() {}
// getters and setters
// hashCode() and equals()
}
The combined-primary-key:
public class TestDataId implements Serializable {
private static final long serialVersionUID = 1L;
private Integer idPartOne;
private Long idPartTwo;
public TestDataId() {}
// getters and setters
// hashCode() and equals()
}
Test-Table was created with the following statement:
CREATE TABLE `testdb`.`testdata`
(`idPartOne` INTEGER NOT NULL AUTO_INCREMENT,
`idPartTwo` BIGINT(20) NOT NULL DEFAULT 0,
`data` VARCHAR(45),
PRIMARY KEY(`idPartOne`, `idPartTwo`))
ENGINE = InnoDB;
Changing the GenerationType to TABLE would make it work, but would generate idPartOne-values in steps of ~32.000. Unfortunately, another application uses this very same database-table without JPA/Hibernate and is nicely incrementing this id-part in steps of 1.
It is required that the id-generation is done in the same way no matter which application stores data into this table (that is, id incrementing of 1). What would be the best solution to achieve this? Hint, we cannot change the other application!
Any help is really appreciated.
Thx,
Markus
Does it work when you annotate the embedded id?
#Embeddable
public class TestDataId
{
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer idPartOne;
private Long idPartTwo;
}
#Entity
public class TestData
{
#EmbeddedId
private TestDataId key;
private String data;
}