Many JavaFX constructors, such as for SimpleStringProperty specify that the first argument should be a bean. I know that one of the qualities of JavaBeans is that they are Serializable. The sample code in The Definitive Guide to Modern Java Clients with JavaFX, has examples where the parameter passed in the bean position is not serializable:
public class Person { // not Serializable
private final StringProperty firstname = new SimpleStringProperty(this, "fistname", "");
private final StringProperty lastname = new SimpleStringProperty(this, "lastname", "");
private final StringProperty notes = new SimpleStringProperty(this, "notes", "sample notes");
Are JavaFX beans required to be Serializable or only to have a public no-args constructor and setters/getters to access private fields?
It is not not mandatory for a Class to be Serializable unless you intend to use serialization on that Class or any Subclass
Related
I have the below data object and CrudRepository implementation. When I use the CrudRepository operations both the key and value get stored as HASH data types when I would like to have them stored as String datatypes. How can I configure this behavior? I tried adding the RedisTemplate bean as below and when debugging I do see that redis template is being used, but for data conversions it uses some other KeyValueAdapter classes instead it seems, and I'm having a hard time configuring that.
#Data
#RedisHash("DATA")
#Accessors(chain = true)
public class Data implements Serializable {
#Id
private String id;
private List<AnotherObject> objects;
private Long last_updated;
}
#Repository
public interface DataCache extends CrudRepository<Data, String> {
}
#Bean
public RedisTemplate<?, ?> redisTemplate(
#Autowired RedisConnectionFactory redisConnectionFactory,
#Autowired ObjectMapper mapper) {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(Object.class);
valueSerializer.setObjectMapper(mapper);
template.setValueSerializer(valueSerializer);
template.setHashKeySerializer(valueSerializer);
template.setDefaultSerializer(new StringRedisSerializer());
template.setEnableDefaultSerializer(false);
template.afterPropertiesSet();
return template;
}
The domain model is
An industry has many companies
A company belongs to an industry
So I have my entity classes:
#Entity
public class Industry {
#Id #GeneratedValue
private Long id;
private String name;
#OneToMany(targetEntity = Company.class, fetch = FetchType.LAZY, mappedBy = "industry")
private Collection<Company> companies = new ArrayList<>(0);
// Getters and setters
}
and
#Entity
public class Company {
#Id #GeneratedValue
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER, optional = false)
private Industry industry;
// Getters and setters
}
My controller:
#RestController
#RequestMapping("/companies")
public class CompaniesController extends ControllerBase {
#RequestMapping(method = RequestMethod.POST)
public Company create(#RequestBody Company company) {
company.getIndustry(); // returns null
// ...
}
}
When I send request POST /companies with request body
{
"name": "Walmart",
"industry": {
"id": 1
}
}
I found that company.getIndustry() always returns null. How can I make the controller accept nested entities?
Entities are session based. They usually work on basis of Lazy loading I.e only the first level is loaded and other attributes are loaded on Demand. You cannot pass it from one layer to other. (service to controller)
The correct way to do it. Have a Value object (a simple class) n the controller. Use it between front end and back end. Send the same value object to service. And use the entity only between Service and DAo layer
public class CompanyVO{
private Long id;
private String name;
private IndustryVO industryVO; // create similar class
// Getters and setters
}
#RestController
#RequestMapping("/companies")
public class CompaniesController extends ControllerBase {
#RequestMapping(method = RequestMethod.POST)
public Company create(#RequestBody CompanyVO companyVO) {
companyVO.getIndustry(); // returns null
// ...
}
}
This may be because you need another Spring message converter instead of the default one. Just add jackson to your pom.xml and Spring will use MappingJackson2HttpMessageConverter.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>
class Person {
private String firstName;
private String lastName;
// getters and setters for firstName, lastName
}
#Test
void test() {
Person p = mock(Person.class);
when(p.getFirstName()).thenReturn("John");
when(p.getLastName()).thenReturn("Peter");
Map<String, Object> someContainerLikeMap = new HashMap<>();
org.springframework.util.ReflectionUtils.doWithFields(p.getClass(), field -> {
someContainerLikeMap.put(field.getName(), field.get(p));
// field.get(p) above, always get null
}
}
I've got two questions:
get by field reflection, field.get(p), always get null;
iteration of fields, what's the best way to just have fields defined in class Person included, that is, firstName, lastName?
Mockito works entirely through subclassing methods automatically with a "proxy object"; it doesn't override, change, or mock fields at all. You'll need a different tool for that.
I would like to know what is the use case of #NamedArg annotation in JavaFX 8
The javadoc does not give us more details,
Javadoc : Annotation that provides information about argument's name.
And no more information, documentation, examples on the internet.
Maybe someone could help ?
Regards.
The #NamedArg annotation allows an FXMLLoader to instantiate a class that does not have a zero-argument constructor.
Technical Background:
The FXMLLoader creates objects using reflection. Typically, if you use a tag corresponding to a class with a constructor taking no arguments, an object is created from that class by calling Class.newInstance(), which invokes the no-argument constructor.
If a class is defined only with constructors that take parameters, then this is problematic. The main issue is that the Java Language Specification does not require the names of parameters (to methods or constructors) to be retained at runtime. This means there's no direct, guaranteed, way for the FXMLLoader to determine which parameter has a given name.
To make this concrete, suppose we define a Person class as follows:
package application;
import javafx.beans.NamedArg;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName ;
private final StringProperty lastName ;
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(this, "firstName", firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
}
// methods....
}
In FXML we might try to create a Person as follows:
<Person firstName="Jacob" lastName="Smith"/>
This won't work, because the FXML loader has no guarantee that the runtime representation of the Person class retains the information as to which constructor parameter is firstName and which is lastName.
Historical background
Java 2.2 defined "Builder" classes corresponding to each control. These builder classes follow the standard builder pattern. When the FXMLLoader encounters a tag referencing a class with no zero-argument constructor, it would use the corresponding builder to create the instance.
Unfortunately, the implementation of the builder classes was flawed, and they were deprecated in JavaFX 8, and will be removed in a later version (probably JavaFX 9). This left a problem for the FXMLLoader, which would no longer have builder classes to rely on for instantiating classes with no zero-argument constructor. A real example is the Color class, which has no zero-argument constructor and will have its builder class removed.
#NamedArgs
The fix for this was to introduce an annotation that is used to retain a name of a method (or constructor) argument at runtime. By reflection, we can query the parameter list of a constructor/method, and get the type (but not the name) of each parameter. It is also possible to query each parameter for any annotations, and get the value of those annotations. So the #NamedArg annotation was introduced specifically for the purpose of retaining a name of a parameter at runtime.
Example
For an example, use the Person class we introduced above:
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName ;
private final StringProperty lastName ;
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(this, "firstName", firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
}
public final StringProperty firstNameProperty() { return firstName; }
public final String getFirstName() { return firstNameProperty().get(); }
public final void setFirstName(final String firstName) { firstNameProperty().set(firstName); }
public final StringProperty lastNameProperty() { return lastName; }
public final String getLastName() { return lastNameProperty().get(); }
public final void setLastName(final String lastName) { lastNameProperty().set(lastName); }
}
If you try to load this using FXML:
Person.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import application.Person?>
<Person firstName="Jacob" lastName="Smith" xmlns:fx="http://javafx.com/fxml/1" />
Main.java:
package application;
import java.io.IOException;
import javafx.fxml.FXMLLoader;
public class Main {
public static void main(String[] args) throws IOException {
Person person = FXMLLoader.load(Main.class.getResource("Person.fxml"));
System.out.println(person.getFirstName()+" "+person.getLastName());
}
}
then you see an error at runtime:
Caused by: java.lang.NoSuchMethodException: application.Person.<init>()
indicating the FXMLLoader is looking for a constructor taking no arguments (Person.<init>()).
In JavaFX 8, you can fix the problem by specifying the name of the parameters with the #NamedArg annotation:
package application;
import javafx.beans.NamedArg;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName ;
private final StringProperty lastName ;
public Person(#NamedArg("firstName") String firstName, #NamedArg("lastName") String lastName) {
this.firstName = new SimpleStringProperty(this, "firstName", firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
}
public final StringProperty firstNameProperty() { return firstName; }
public final String getFirstName() { return firstNameProperty().get(); }
public final void setFirstName(final String firstName) { firstNameProperty().set(firstName); }
public final StringProperty lastNameProperty() { return lastName; }
public final String getLastName() { return lastNameProperty().get(); }
public final void setLastName(final String lastName) { lastNameProperty().set(lastName); }
}
This will allow the FXMLLoader to load the class as required.
Note that you can also fix the issue by defining a builder class, and that this also works in JavaFX 2.0 and later. The JavaFX team decided (probably correctly) that using this approach in a way that didn't suffer from the bugs that existed in the initial implementation of the builders would add too much bloat to the codebase of the framework.
package application;
public class PersonBuilder {
private String firstName ;
private String lastName ;
private PersonBuilder() { }
public static PersonBuilder create() {
return new PersonBuilder();
}
public PersonBuilder firstName(String firstName) {
this.firstName = firstName ;
return this ;
}
public PersonBuilder lastName(String lastName) {
this.lastName = lastName ;
return this ;
}
public Person build() {
return new Person(firstName, lastName);
}
}
Clearly if you are using JavaFX 8, the constructor annotation approach is much less work.
References:
Proposal to deprecate builders
Tweak request to add constructor annotations
Builder pattern
FXML documentation (discusses builders, but not #NamedArg)
Request to add documentation on #NamedArgs to "Introduction to FXML" document
I need to set some private fields in an object using another object's fields. Those two objects may not be instances of same class.
What I see from a short reading, I can use Apache's BeanUtils and Spring's ReflectionUtils for that. I couldn't find a satisfying explanation for them regarding security, performance, support etc.
The solution will be used in production environment too, so I need a concrete solution.
Which approach do you suggest for such a task.
I think you need use just the BeanUtils library. See my sample, i do a copy properties from CustomerBean to SellerBean.
package testes.beanutils;
import org.apache.commons.beanutils.BeanUtils;
public class Main {
public static void main(String[] args) throws Exception {
Customer customer = new Customer();
customer.setId((long)1);
customer.setName("Bruno");
customer.setLastname("Tafarelo");
Seller seller = new Seller();
BeanUtils.copyProperties(seller, customer);
System.out.println(customer);
System.out.println(seller);
}
}
class Customer {
private Long id;
private String name;
private String lastname;
//getters and setters
//toString
}
class Seller {
private Long id;
private String name;
private int sales;
//getters and setters
//toString
}