I am trying to fill JavaFx TableView Columns with mock data, but I keep getting a reflection error, even though I think I'm following Bean conventions correctly:
// Data model
class SensorTableEntry {
SensorTableEntry(Integer id, String man, String type, String addr) {
this.id = new SimpleIntegerProperty(id);
this.manufacturer = new SimpleStringProperty(man);
this.type = new SimpleStringProperty(type);
this.btAddress = new SimpleStringProperty(addr);
}
private IntegerProperty id;
public Integer getId() { return idProperty().get(); }
public void setId(Integer value) { idProperty().set(value); }
public IntegerProperty idProperty() { return id; }
private StringProperty manufacturer;
public void setManufacturer(String value) { manufacturerProperty().set(value); }
public String getManufacturer() { return manufacturerProperty().get(); }
public StringProperty manufacturerProperty() { return manufacturer; }
private StringProperty type;
public void setType(String value) { typeProperty().set(value); }
public String getType() { return typeProperty().get(); }
public StringProperty typeProperty() { return type; }
private StringProperty btAddress;
public void setBtAddress(String value) { btAddressProperty().set(value); }
public String getBtAddress() { return btAddressProperty().get(); }
public StringProperty btAddressProperty() { return btAddress; }
}
// More code before this...
// Actual table inside the controller
ObservableList<SensorTableEntry> sensorEntries = FXCollections.observableArrayList(
new SensorTableEntry(1, "manufacturer", "type", "00:00:00:00:00:00")
);
TableView<SensorTableEntry> table = new TableView<SensorTableEntry>();
TableColumn<SensorTableEntry,Integer> idCol = new TableColumn<SensorTableEntry,Integer>("ID");
idCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,Integer>("id"));
TableColumn<SensorTableEntry,String> manufacturerCol = new TableColumn<SensorTableEntry,String>("Manufacturer");
manufacturerCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("manufacturer"));
TableColumn<SensorTableEntry,String> typeCol = new TableColumn<SensorTableEntry,String>("Type");
typeCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("type"));
TableColumn<SensorTableEntry,String> btAddressCol = new TableColumn<SensorTableEntry,String>("Bluetooth Address");
btAddressCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("btAddress"));
table.setItems(sensorEntries);
table.getColumns().addAll(
idCol,
manufacturerCol,
typeCol,
btAddressCol
);
pane.getChildren().add(table);
I have checked other answers to similar questions like:
Javafx PropertyValueFactory not populating Tableview
JavaFx TableView not filling all required columns
Javafx tableview not showing data in all columns
But no matter how much I check I don't seem to find where my naming went wrong. Am I missing something?
The exception I get is:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class SensorTableEntry with modifiers "public"
at com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:200)
Your properties must be fully accessible so their getter and their owner class must both be public.
So simply replace this:
class SensorTableEntry {
With this:
public class SensorTableEntry {
Since you are using JavaFX properties in your model, you can use actual implementations of the callback (with lambda expressions for brevity) and avoid reflection completely. Note that IntegerProperty implements Property<Number>, not Property<Integer>, so you will need to fix the types (see JavaFX Properties in TableView):
TableColumn<SensorTableEntry,Number> idCol = new TableColumn<SensorTableEntry,Number>("ID");
idCol.setCellValueFactory(cellData -> cellData.getValue().idProperty());
TableColumn<SensorTableEntry,String> manufacturerCol = new TableColumn<SensorTableEntry,String>("Manufacturer");
manufacturerCol.setCellValueFactory(cellData -> cellData.getValue().manufacturerProperty());
TableColumn<SensorTableEntry,String> typeCol = new TableColumn<SensorTableEntry,String>("Type");
typeCol.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
TableColumn<SensorTableEntry,String> btAddressCol = new TableColumn<SensorTableEntry,String>("Bluetooth Address");
btAddressCol.setCellValueFactory(cellData -> cellData.getValue().btAddressProperty());
This is generally a much better approach: the compiler will check that the properties exist and are of the correct type, and since you are not relying on reflection to evaluate the cell values, performance will be better (probably negligibly, but nevertheless...).
One other aside: in the JavaFX property pattern, the methods for the primitive wrapper properties should use primitive types, not object wrapper types, i.e.:
class SensorTableEntry {
SensorTableEntry(int id, String man, String type, String addr) {
this.id = new SimpleIntegerProperty(id);
this.manufacturer = new SimpleStringProperty(man);
this.type = new SimpleStringProperty(type);
this.btAddress = new SimpleStringProperty(addr);
}
private IntegerProperty id;
public int getId() { return idProperty().get(); }
public void setId(int value) { idProperty().set(value); }
public IntegerProperty idProperty() { return id; }
// existing code...
}
Related
My PoIs class:
public class PoIs {
private Integer location_id;
private String location_name;
private String location_address;
public PoIs() {}
public PoIs(Integer location_id, String location_name, String location_address) {
this();
this.location_id = location_id;
this.category_id = category_id;
this.location_name = location_name;
this.location_address = location_address;
}
public Integer get_location_id() {
return location_id;
}
public void set_location_id(Integer location_id) {
this.location_id = location_id;
}
public String get_location_name() {
return location_name;
}
public void set_location_name(String location_name) {
this.location_name = location_name;
}
public String get_location_address() {
return location_address;
}
public void set_location_address(String location_address) {
this.location_address = location_address;
}
I populate PoIs with informatision from a sqlite database:
final PoIs p = new PoIs(Integer.parseInt(row.get(0).toString()), row.get(1).toString(), row.get(2).toString());
and at a moment intend to save them on a firabase database:
FIREBASE_REFERENCE.child("PoI_"+ p.get_location_id()).setValue(p)
.addOnCompleteListener(t -> {
final boolean isSuccessful = t.isSuccessful();
final String msg = !isSuccessful
? getResources().getString(R.string.fb_error)
: getResources().getString(R.string.fb_success);
});
All work perfect except that my firebase fields start with an underscore. Instead location_id, location_name, location_address I have _location_id, _location_name, _location_address. I can't understand why this happening. Any ideea how to resolve this issue?
Firebase uses JavaBean naming conventions when mapping from properties in your code to properties in the database. In that convention a method like get_location_name is the getter for a property called _location_name.
If you want the property in the database to be location_name, that'd be a getter getLocation_name. Alternatively, you can use a #PropertyName("location_name")) annotation on all accessors (so the getter/setter function and/or the public field) to indicate the explicit property name you want in the database.
My Enum type ProductType is properly saving to XML but it doesn't want to unmarshal when opening the file.
I made EnumAdapter:
public class EnumAdapter extends XmlAdapter<String, ProductType>
{
#Override
public ProductType unmarshal(String value) throws Exception {
try {
return ProductType.valueOf(value);
}
catch(Exception e) {
throw new JAXBException(e);
}
}
#Override
public String marshal(ProductType value) {
return value.toString();
}
}
My Product class:
public class Product {
private final IntegerProperty ilosc; //quantity
private final StringProperty nazwa; //name
private final ObjectProperty<ProductType> typ; //type
private final BooleanProperty dostepnosc;
public Product()
{
this(null, 0, ProductType.ALKOHOL, true);
}
public Product(String nazwa, int ilosc, ProductType typ, boolean dostepnosc) {
this.nazwa = new SimpleStringProperty(nazwa);
this.ilosc = new SimpleIntegerProperty(ilosc);
this.typ = new SimpleObjectProperty<>(typ);
this.dostepnosc = new SimpleBooleanProperty(dostepnosc);
}
.
.
.
#XmlJavaTypeAdapter(EnumAdapter.class)
public ProductType getTyp() {
return typ.get();
}
After opening the XML in my app enum is always setting to the value from default constructor (which is ALCOHOL, if I change it, enum is setting to whatever it is). I also know that marshalling from EnumAdapter works properly, I can change it to whatever I want. Please help.
I solved it, I was missing proper setting function:
public void setTyp(ProductType type){
this.typ.setValue(type);
I have used One-to-Many Mapping in my project. I have stored a list of clicks for every user.
But when I retrieve the list by calling getClicks() methodm Hibernate returns list in different format.
Something like this.
"[com.zednx.tech.persistence.Click#29df9a77]"
So I tried Reading Every value from the list and assign to a new List.
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
clicks.add(temp);
}
But when i print the items of new List it stills prints the same way.
I need to build a JSON from the resulting String of this list.
So if the list is returned in format, it wont help me.
I couldn't find anything regarding this except How to pretty print Hibernate query results?
I tried Arrays.ToString(Object o). But it doesn't work.
GSON builder part-
Gson gson = new GsonBuilder()
.registerTypeAdapter(Click.class, new MyTypeAdapter<Click>())
.create();
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
temp.setCom_to_recieve(c.getCom_to_recieve());
temp.setStore_name(c.getStore_name());
temp.setT_date(c.getT_date());
temp.setT_status(c.getT_status());
temp.setT_ticket(c.getT_ticket());
temp.setUid(c.getUid());
System.out.println(c.toString());
clicks.add(temp);
}
String json = gson.toJson(clicks, Click.class);
Click.java
#Entity
#Table(name="click")
public class Click {
#Id
#Column(name="t_ticket")
private String t_ticket;
#Column(name="uid",nullable=false)
private long uid;
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
#ManyToOne
#JoinColumn(name="uid",
insertable=false, updatable=false,
nullable=false)
private Earning earning;
#Column(name="store_name")
private String store_name;
#Column(name="t_status")
private String t_status;
#Column(name="aff_source")
private String aff_source;
#Column(name="com_to_recieve")
private float com_to_recieve;
#Column(name="t_date")
private Date t_date;
#Column(name="cb_to_award")
private float cb_to_award;
#Column(name="cb_type")
private String cb_type;
public String getT_ticket() {
return t_ticket;
}
public void setT_ticket(String t_ticket) {
this.t_ticket = t_ticket;
}
public Earning getEarning() {
return earning;
}
public void setEarning(Earning earning) {
this.earning = earning;
}
public String getStore_name() {
return store_name;
}
public void setStore_name(String store_name) {
this.store_name = store_name;
}
public String getT_status() {
return t_status;
}
public void setT_status(String t_status) {
this.t_status = t_status;
}
public String getAff_source() {
return aff_source;
}
public void setAff_source(String aff_source) {
this.aff_source = aff_source;
}
public float getCom_to_recieve() {
return com_to_recieve;
}
public void setCom_to_recieve(float com_to_recieve) {
this.com_to_recieve = com_to_recieve;
}
public Date getT_date() {
return t_date;
}
public void setT_date(Date t_date) {
this.t_date = t_date;
}
public float getCb_to_award() {
return cb_to_award;
}
public void setCb_to_award(float cb_to_award) {
this.cb_to_award = cb_to_award;
}
public String getCb_type() {
return cb_type;
}
public void setCb_type(String cb_type) {
this.cb_type = cb_type;
}
Any Help is appreciated.
You need to implement a toString method, as your current Click class likely doesn't have one, so it just prints as the name of the class and instance identifier.
Okay, I could solve my problem finally.
I made another POJO without any annotations and Mapped the List items to that POJO class.
I think the problem was with Annotation of mapping on another class which I had in original POJO.
Also getString() method only helps in changing format of identifier. So basically it has nothing to do with JSON building unless you format getString() in form of JSON.
Hope it helps. If anyone wants new temp POJO I made I can post it if requested.
Thanks.
So in class we always use the following syntax. Correct me if i am wrong but this is a bean because it class use getters/setters. It has an nullary constructor and the class implements serializable.
// option 1
private int customerID ;
public CustomerDTO ()
{
this(0);
}
public CustomerDTO(int customerID) {
setCustomerID(customerID);
}
public void setCustomerID(int customerID) {
this.customerID = customerID;
}
public int getCustomerID() {
return customerID;
}
But today i came across something similar like this. i needed to import
import javafx.beans.property.SimpleStringProperty;
But what is the main difference between option 1 and 2.
When should i use option 1 or option 2
And which one is better or does it depends on the situation.
// option 2
private final IntegerProperty customerID;
public CustomerDTO ()
{
this(null);
}
public CustomerDTO(IntegerProperty customerID) {
this.customerID = new SimpleIntegerProperty();
}
public IntegerProperty getCustomerID() {
return customerID;
}
public void setCustomerID(int customerID) {
this.customerID.set(customerID);
}
Option 2 is used when you are building JavaFX application and want to bind your model with gui.
Example:
public class Foo {
private final StringProperty foo = new SimpleStringProperty();
public String getFoo() {
return foo.get();
}
public StringProperty fooProperty() {
return foo;
}
public void setFoo(String foo) {
this.foo.set(foo);
}
}
public class FooController {
#FXML
private TextField fooTextField;
private final Foo foo = new Foo();
#FXML
public void initialize() {
foo.fooProperty().bindBidirectional(fooTextField.textProperty());
}
}
public CustomerDTO(IntegerProperty customerID) { makes no sense, a property is a final class member which encapsulates a value, this value can be set via setters and get via setters, in JavaFX controller classes it is advisable to also implement a getter for the ReadOnlyObjectProperty or ReadOnlyIntegerProperty in your case, this can be done via ReadOnlyIntegerWrapper and its getReadOnlyProperty method. This enables the developor to bind to values from other classes whilst also ensuring that the value exists at any time, JavaFX Bindings are a pretty elegant and object-oriented method of data-encapsulation.
Your "option 2" actually is flawed since it allows property-redefinition which breaks this concept and makes the property itself useless. It will also break GUI functionality except if the property itself can not be redefined, see the accepted answer
two class:
public class BaseDo<K> {
protected K id;
public K getId() {
return id;
}
public void setId(K id) {
this.id = id;
}
}
public class BeanDo extends BaseDo<Integer> {
private String beanName;
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
I want use reflect to implment like this:
BeanDo beanDo = new BeanDo();
beanDo.setId("string here");
Integer type reference String type value.
Generics in Java are not used at runtime, so as far as the java runtime is concerned you're ID field is of type Object and so can be set to any value regardless of the generics. That said, doing so is a bad idea since anything assuming the generic contract will fail.
You can set the field by reflection as follows:
BeanDo beanDo = new BeanDo();
Method method = BeanDo.getClass().getMethod("setId", Object.class);
method.invoke(beanDo, "SomeRandomString");
That said, doing this is an extreamly bad idea because any code compile against BeanDo will assume that the id is an integer not a String. So any code like beanDo.getId() will fail with a class cast exception because it's not actually an integer.
Like the other posters, I'm somewhat in the dark about what you're trying to achieve.
Something like this?
public class BaseDo<K> {
protected K id;
public K getId() {
return id;
}
public void setId(K id) {
this.id = id;
}
}
public class BeanDo extends BaseDo<Integer> {
private String beanName;
public void setId(String id) {
setId(Integer.parseInt(id));
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Now you can use something like this:
BeanDo beanDo = new BeanDo();
beanDo.setId("2");
What about this:
BeanDo beando = new BeanDo();
beando.setId("string there".hashCode());
I don't quite get what you mean with "I want to use reflect to implement this" though.
I guess you want something like this:
BeanDo doer = ... // get it from somewhere
String id = ... // get it from somewhere else too.
// and you want to set id to the doer bean.
reflectionMagicSetId( doer, id );
And have the method like:
private void reflectionMagicSetId( BandDo doer, String id ) {
/// do some magic here?
}
If that's what you want, what I give you works perfectly.
private void reflectionMagicSetId( BandDo doer, String id ) {
doer.setId( id == null ? 0 : id.hashCode() );
}
If you wann use integer then parse the string to integer as it will contain the integer and use that integer in the calling function argument
It seems like a subclass about the only way to be able to set a string, but still guarantee that anyone who's already calling getId() gets the Integer they expect. Something like this:
public class StringBeanDo extends BeanDo {
private String stringId;
public String getStringId()
{
return stringId;
}
public void setId( Integer val )
{
super.setId( val );
stringId = Integer.toString( val );
}
public void setId( String str )
{
stringId = str;
super.setId( convertStringToInteger( str )); // Do this however you like.
}
}
The implementation of convertStringToInteger would be up to you (it'll depend on what this ID is being used for). The key here is that you're maintaining TWO IDs, and keeping them in sync, so that older code can still limp along to some extent.