How to limit length of Varchar between 2 and 100 - mysql

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.

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

Can't use String as #Id with SpringData

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;

Fetch related entities as Base Type

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.

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.

Simplest one-to-many Map case in Hibernate doesn't work in MySQL

I think this is pretty much the simplest case for mapping a Map (that is, an associative array) of entities.
#Entity
#AccessType("field")
class Member {
#Id
protected long id;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
#MapKey(name = "name")
private Map<String, Preferences> preferences
= new HashMap<String, Preferences>();
}
#Entity
#AccessType("field")
class Preferences {
#ManyToOne Member member;
#Column String name;
#Column String value;
}
This looks like it should work, and it does, in HSQL. In MySQL, there are two problems:
First, it insists that there be a table called Members_Preferences, as if this were a many-to-many relationship.
Second, it just doesn't work: since it never populates Members_Preferences, it never retrieves the Preferences.
[My theory is, since I only use HSQL in memory-mode, it automatically creates Members_Preferences and never really has to retrieve the preferences map. In any case, either Hibernate has a huge bug in it or I'm doing something wrong.]
And of course, I sweat the problem for hours, post it here, and a minute later...
Anyway, the answer is the mappedBy element of the #OneToMany annotation:
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="member")
#MapKey(name = "name")
private Map<String, Preferences> preferences
= new HashMap<String, Preferences>();
Which makes a certain sense: which field in the Many entity points back to the One entity? Even allowing that looking for a matching #ManyToOne field was too error prone, I think that what they did do (assuming the existence of a mapping table) makes even worse.