I want to add rows dynamically without disturbing other data in a the table ( data is hardcoded in a Object[][] dataForTable. Don't want to persist data in database). I didn't find any method from TableModel or Table itself to do so. Is there any method to achieve this without replacing Object[][] dataForTable or any other way that ?
I have other situation. At any time I want one extra row in the Table that will be empty. And when putting values to this row another empty row will be created dynamically.
Update: I re-evaluated this answer and it includes some bugs and conceptual problems. We decided to add the option to add/remove rows from the Table into the DefaultTableModel. So you can just use ((DefaultTableModel)table).addRow("Col1", "Col2", "Col3"); Just make sure the number of columns is the exact match to the table column count.
Original answer below:
The default table model isn't mutable which is probably an omission on our part. Something like this should probably work (haven't tested):
public class MyTableModel implements TableModel {
private ArrayList<Object[]) data;
private String[] columnNames;
private EventDispatcher dispatcher = new EventDispatcher();
private boolean editable;
public MyTableModel(String[] columnNames, Object[][] data) {
this(columnNames, data, false);
}
public MyTableModel(String[] columnNames, Object[][] data, boolean editable) {
this.data = new ArrayList<Object[]>();
for(Object[] o : data) {
this.data.add(o);
}
this.columnNames = columnNames;
this.editable = editable;
}
/**
* #inheritDoc
*/
public int getRowCount() {
return data.length;
}
/**
* #inheritDoc
*/
public int getColumnCount() {
return columnNames.length;
}
/**
* #inheritDoc
*/
public String getColumnName(int i) {
return columnNames[i];
}
/**
* #inheritDoc
*/
public boolean isCellEditable(int row, int column) {
return editable;
}
/**
* #inheritDoc
*/
public Object getValueAt(int row, int column) {
try {
return data.get(row)[column];
} catch(ArrayIndexOutOfBoundsException err) {
// not the best situation but quite useful for the resource editor
//err.printStackTrace();
return "";
}
}
/**
* #inheritDoc
*/
public void setValueAt(int row, int column, Object o) {
data.get(row)[column] = o;
dispatcher.fireDataChangeEvent(column, row);
}
/**
* #inheritDoc
*/
public void addDataChangeListener(DataChangedListener d) {
dispatcher.addListener(d);
}
/**
* #inheritDoc
*/
public void removeDataChangeListener(DataChangedListener d) {
dispatcher.removeListener(d);
}
public void addRow(Object[] row) {
data.add(row);
dispatcher.fireDataChangeEvent(-1, row);
}
}
Related
So the thing that i want to happen, is making the tableview update the data in the database after editing it. I wanted to use the SetOnEditCommit method here. The cell editing does work, but it never gets updated, with no error either. In the first place im a bit clueless if this method is actually efficient (probably not), since its hard to find some sources for this specific thing. And the sources that i found weren't really helpful. So it would be nice if someone had an idea as to why it doesn't update, or maybe provide an alternate option here.
The mentioned part:
columnType.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<UserDetails, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<UserDetails, String> event) {
updataData();
}
});
tableview.setItems(null);
tableview.setItems(data);
}
public void updataData() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
Statement con = connection.createStatement();
//connection
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
String data1 = (String) col.getCellObservableValue(row).getValue();
//cell
UserDetails row1 = tableview.getSelectionModel().getSelectedItem();
c1 = row1.getId();
//row
//tableview variables
con.execute("UPDATE gevonden_bagage SET type = 'data1' WHERE koffer_id = 'c1' ");
//Query
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
}
//get connection, get celldata, get id data from first row, update cell with selected id
full controller class:
package simple;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
/**
*
* #author admin
*/
public class FXMLUserController extends SimpleController implements Initializable {
#FXML
public TableView<UserDetails> tableview;
#FXML
public TableColumn<UserDetails, String> columnId;
#FXML
public TableColumn<UserDetails, String> columnType;
#FXML
public TableColumn<UserDetails, String> columnKleur;
#FXML
public TableColumn<UserDetails, String> columnLuchthaven;
#FXML
public TableColumn<UserDetails, String> columnKenmerken;
#FXML
public TableColumn<UserDetails, String> columnStatus;
#FXML
public TableColumn<UserDetails, String> columnDatum;
#FXML
private Button btnLoad;
//declare observable list for database data
private ObservableList<UserDetails> data;
private DbConnection dc;
String c1;
#FXML
//strings for getRow method
#Override
public void initialize(URL url, ResourceBundle rb) {
dc = new DbConnection();
loadDataFromDatabase();
}
#FXML
public void loadDataFromDatabase() {
try {
Connection conn = dc.Connect();
data = FXCollections.observableArrayList();
// Execute query and store result in a resultset
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM gevonden_bagage");
while (rs.next()) {
//get strings
data.add(new UserDetails(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5),
rs.getString(6), rs.getString(7)));
}
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
//Set cell values to tableview.
tableview.setEditable(true);
tableview.getSelectionModel().setCellSelectionEnabled(true);
columnType.setCellFactory(TextFieldTableCell.forTableColumn());
columnKleur.setCellFactory(TextFieldTableCell.forTableColumn());
columnLuchthaven.setCellFactory(TextFieldTableCell.forTableColumn());
columnKenmerken.setCellFactory(TextFieldTableCell.forTableColumn());
columnStatus.setCellFactory(TextFieldTableCell.forTableColumn());
columnDatum.setCellFactory(TextFieldTableCell.forTableColumn());
//makes columns editable
columnId.setCellValueFactory(new PropertyValueFactory<>("id"));
columnType.setCellValueFactory(new PropertyValueFactory<>("type"));
columnKleur.setCellValueFactory(new PropertyValueFactory<>("kleur"));
columnLuchthaven.setCellValueFactory(new PropertyValueFactory<>("luchthaven"));
columnKenmerken.setCellValueFactory(new PropertyValueFactory<>("kenmerken"));
columnStatus.setCellValueFactory(new PropertyValueFactory<>("status"));
columnDatum.setCellValueFactory(new PropertyValueFactory<>("datum"));
columnType.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<UserDetails, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<UserDetails, String> event) {
updataData();
}
});
tableview.setItems(null);
tableview.setItems(data);
}
public void updataData() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
Statement con = connection.createStatement();
//connection
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
String data1 = (String) col.getCellObservableValue(row).getValue();
//cell
UserDetails row1 = tableview.getSelectionModel().getSelectedItem();
c1 = row1.getId();
//row
//tableview variables
con.execute("UPDATE gevonden_bagage SET type = 'data1' WHERE koffer_id = 'c1' ");
//Query
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
}
//get connection, get celldata, get id data from first row, update cell with selected id
#FXML
public void getRow() {
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
// this gives the value in the selected cell:
String data1 = (String) col.getCellObservableValue(row).getValue();
System.out.println(data1);
//CURRENTLY UNUSED METHOD
}
}
Model class:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author admin
*/
public class UserDetails {
private final StringProperty id;
private final StringProperty type;
private final StringProperty kleur;
private final StringProperty luchthaven;
private final StringProperty kenmerken;
private final StringProperty status;
private final StringProperty datum;
//Default constructor
public UserDetails(String id, String type, String kleur, String luchthaven, String kenmerken, String status, String datum) {
this.id = new SimpleStringProperty(id);
this.type = new SimpleStringProperty(type);
this.kleur = new SimpleStringProperty(kleur);
this.luchthaven = new SimpleStringProperty(luchthaven);
this.kenmerken = new SimpleStringProperty(kenmerken);
this.status = new SimpleStringProperty(status);
this.datum = new SimpleStringProperty(datum);
}
//getters
public String getId() {
return id.get();
}
public String getType() {
return type.get();
}
public String getKleur() {
return kleur.get();
}
public String getLuchthaven() {
return luchthaven.get();
}
public String getKenmerken() {
return kenmerken.get();
}
public String getStatus() {
return status.get();
}
public String getDatum() {
return datum.get();
}
//setters
public void setId(String value) {
id.set(value);
}
public void setType(String value) {
type.set(value);
}
public void setKleur(String value) {
kleur.set(value);
}
public void setLuchthaven(String value) {
luchthaven.set(value);
}
public void setKenmerken(String value) {
kenmerken.set(value);
}
public void setStatus(String value) {
status.set(value);
}
public void setDatum(String value) {
datum.set(value);
}
//property values
public StringProperty idProperty() {
return id;
}
public StringProperty typeProperty() {
return type;
}
public StringProperty kleurProperty() {
return kleur;
}
public StringProperty luchthavenProperty() {
return luchthaven;
}
public StringProperty kenmerkenProperty() {
return kenmerken;
}
public StringProperty statusProperty() {
return status;
}
public StringProperty datumProperty() {
return datum;
}
}
From the TableView documentation:
By default the TableColumn edit commit handler is non-null, with a
default handler that attempts to overwrite the property value for the
item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object) method is passed in the new value, and this is
passed along to the edit commit handler via the CellEditEvent that is
fired. It is simply a matter of calling
TableColumn.CellEditEvent.getNewValue() to retrieve this value.
It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler) with your own
EventHandler, then you will be removing the default handler. Unless
you then handle the writeback to the property (or the relevant data
source), nothing will happen.
So the problem is that by setting the onEditCommit on columnType, you remove the default handler that actually updates typeProperty in the UserDetails instance. Consequently
String data1 = (String) col.getCellObservableValue(row).getValue();
gives the old value, and your update to the database won't change anything.
Additionally, you have errors in the way you create the SQL statement. You are making the id in the WHERE clause the literal value 'c1' (instead of the value contained in the variable c1, and similarly setting the value of type to the literal value 'data1', instead of the value in the variable data1.
Here is a fix, along with some simplification of the code and some better practices for avoiding SQL injection attacks:
columnType.setOnEditCommit(event -> {
UserDetails user = event.getRowValue();
user.setType(event.getNewValue());
updateData("type", event.getNewValue(), user.getId());
});
and then
private void updateData(String column, String newValue, String id) {
// btw it is way better to keep the connection open while the app is running,
// and just close it when the app shuts down....
// the following "try with resources" at least makes sure things are closed:
try (
Connection connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
PreparedStatement stmt = connection.prepareStatement("UPDATE gevonden_bagage SET "+column+" = ? WHERE koffer_id = ? ");
) {
stmt.setString(1, newValue);
stmt.setString(2, id);
stmt.execute();
} catch (SQLException ex) {
System.err.println("Error");
// if anything goes wrong, you will need the stack trace:
ex.printStackTrace(System.err);
}
}
i have problem, i would to add prototype array to database but this show me this error:
Expected argument of type "AppBundle\Entity\Tag", "array" given
...
Post ->setTag (array(array('value' => 'test'), array('value' => 'tess')))
here is my setter for tag:
public function setTag(\AppBundle\Entity\Tag $tag = null)
{
$this->tag = $tag;
return $this;
}
I Have two entities with relation, here relation:
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="post")
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
*/
private $tag;
public function setTag(\AppBundle\Entity\Tag $tag = null)
{
$this->tag = $tag;
return $this;
}
}
and tag:
class Tag
{
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="tag")
*/
private $post;
}
Source:
http://snipet.co.uk/kR
http://snipet.co.uk/gcf
http://snipet.co.uk/0VI
You're trying to model a bidirectional many-to-many relation between Post and Tag.
So, first of all, your getters need to return a collection of objects, and your setters need to accept a collection of objects - not only one single object as in your code (your setTag method accepts a parameter of type Tag - but you need an array-like parameter).
Secondly, the Doctrine framework does not work with simple PHP arrays, but with implementations of \Doctrine\Common\Collections\Collection.
Next, you need to initialize your collection fields in the constructors of your entity classes with an implementation of the Collection class - you can use \Doctrine\Common\Collections\ArrayCollection.
So your entity classes should look rather like this:
/**
* #ORM\Entity
*/
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="posts")
* #ORM\JoinTable(name="posts_tags")
*/
private $tags;
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getTags()
{
return $this->tags;
}
public function setTags(\Doctrine\Common\Collections\Collection $tags)
{
$this->tags = $tags;
}
}
/**
* #ORM\Entity
*/
class Tag
{
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="tags")
*/
private $posts;
public function __construct()
{
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getPosts()
{
return $this->posts;
}
public function setPosts(\Doctrine\Common\Collections\Collection $posts)
{
$this->posts = $posts;
}
}
I strongly advise you to read once again the documentation of the Doctrine framework, how to annotate your entities, and how to model relations: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html
Consider below example:
public class sample{
private Map myMap;
public Map getMap(){
return myMap;
}
}
In above example, we are returning the map to some other calling class. So my question is how we can say that this class encapsulates/protects its data. The Map that will be returned will be available for modification by other classes.
Thanks,
Rajan
Consider this class Person, which have 2 attributes (name and age).
package app;
/**
*
* #author salathielgenese
*/
public final class Person
{
public Person()
{
setAge(age);
setName(name);
}
public Person(String name, long age)
{
setName(name);
setAge(age);
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public long getAge()
{
return age;
}
public void setAge(long age)
{
this.age = age;
}
private String name;
private long age;
}
Now imagine that some one (let say the calling class) set the age to -19. This will generate inconsistency in your that.
So when you protect your data, your controlling wich kind of action are made possible these data.
You may decide that if the given age is lower than 0 then the age will be set to 0 for example. The code may become...
public void setAge(long age)
{
this.age = age;
if (age < 0)
{
this.age = 0;
}
}
You can do the same with name attribute to prevent setting it to null.
public void setName(String name)
{
this.name = name;
if (name == null || name == "")
{
this.name = "NO NAME";
}
}
We'll say that encapsulation help protecting data.
··························································································
Now let's imagine a class called Carpenter. When you need a table, you just ask it to him. Thus the Carpenter class should provide a method which takes a description of the table you need, and return the build table. Assuming that the method is called buildTable, this method will be declared with public access because it's useful to call it from another Class.
When you ask to the Carpenter to build up your Table, he will need to check some material as well as saw, needle and so on and so far. We (calling class) don't care about this internal mechanism and all methods as well as attributes involved in this process will be declared with private access. i.e to prevents external classes from modifying them, i.e to encapsulate our fields and methods for better protection.
Encapsulating a field let us control access to our data.
Comming back to your code, giving public access to getMap() doesn't prevent calling class to modify its content.
Now look at this Example
Person.java
package app;
/**
*
* #author salathielgenese
*/
public final class Person
{
public Person()
{
setAge(age);
setName(name);
}
public Person(String name, long age)
{
setName(name);
setAge(age);
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
if (name == null || name == "")
{
this.name = "NO NAME";
}
}
public long getAge()
{
return age;
}
public void setAge(long age)
{
this.age = age;
if (age < 0)
{
this.age = 0;
}
}
#Override
public String toString()
{
return "Person{" + "name=" + name + ", age=" + age + '}';
}
private String name;
private long age;
}
Example.java
package app;
/**
*
* #author salathielgenese
*/
public class Example
{
public Example()
{
}
public Example(Person person)
{
this.person = person;
}
public Person getPerson()
{
return person;
}
public void setPerson(Person person)
{
this.person = person;
}
private Person person;
}
**Main class (Loader.java)
package app;
/**
*
* #author salathielgenese
*/
public class Loader
{
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
// Instantiate an Example with a new created Person
Example example = new Example(new Person("Rajan", 19));
// Retrive person in example and print its description (printing result of implicit call to person.toString() )
Person person = example.getPerson();
System.out.println(person);
// Assigning a new reference to the variable **person** and print its description
person = new Person("Salathiel", 20);
System.out.println(person);
// Print description of Person containning in Example instance
System.out.println(example.getPerson());
}
}
If you look closed this code, you'll understand that you can change attribute of your Map but not the reference to it.
Maybe you can use an unmodifiable map from Java Collection API's :
public class sample{
private Map myMap;
public Map getMap(){
return Collections.unmodifiableMap(myMap));
}
}
I have a whole database shown in a JTable and I added a print button but what it does, is to convert the data in the JTable into a .csv file, commands excel to open it and the user can print it, but it looks pretty ugly. Is there a way to send a JTable component to printer?
There is a method under JTable called print(). Will save you alot. See below :-
package com.tanyasis.librarymanager;
import java.awt.HeadlessException;
import java.awt.print.PrinterException;
import java.text.MessageFormat;
import javax.swing.JTable;
/**
* Used to provide printing information and adding information that might be
* important in a page such as page header, contents and footer. This class
* makes sure that all contents of a table fit in the given page.
*
* #author Tanyasis Mwanik
*
*/
public class PrintTable {
private JTable table;
private MessageFormat headerFormat, footerFormat;
/**
* Prints the table and provide post printing information to the user if it
* was succesful
*
* #param table
* <code>JTable</code> to be printed
* #param tableTitle
* <code>String</code> to be used as the table header/title
*/
public PrintTable(JTable table, String tableTitle) {
// TODO Auto-generated constructor stub
this.setTable(table);
// Sets the table header
headerFormat = new MessageFormat(tableTitle);
// Sets the table footer
footerFormat = new MessageFormat("Page {0}");
try {
boolean complete = table.print(JTable.PrintMode.FIT_WIDTH,
headerFormat, footerFormat, true, null, true, null);
if (complete) {
new ConfirmationClass(
"<html><h2>Records Printed Successfully</h2></html>");
}
} catch (HeadlessException | PrinterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* #return the table
*/
public JTable getTable() {
return table;
}
/**
* #param table
* the table to set
*/
public void setTable(JTable table) {
this.table = table;
}
}
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//TODO Auto-generated method stub
MessageFormat header=new MessageFormat("Your Invoice");
MessageFormat footer=new MessageFormat("Page");
try
{
table.print(JTable.PrintMode.FIT_WIDTH, header, footer);
}
catch(Exception ae)
{
System.err.println("Error printing: " + ae.getMessage());
}
}
});
It might help :)
ive been trying to figure this out for 2 hours now and i cant seem to understand what went wrong.
I am using Symfony2 and FOSUserBundle.
I created a User entity which extends FOSUserBundle's BaseUser class. Within this User entity, i have 3 variables, id, my_mentors and my_mentees. More details are below:
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="my_mentees")
*/
protected $my_mentors;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="my_mentors")
* #ORM\JoinTable(name="mentor_and_mentee_relationship",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="mentors_or_mentees_user_id", referencedColumnName="id")}
* )
*/
protected $my_mentees;
public function __construct()
{
parent::__construct();
$this->my_mentors = new ArrayCollection();
$this->my_mentees = new ArrayCollection();
}
public function __toString()
{
return $this->getUsername();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add my_mentors
*
* #param Fitness\FitBundle\Entity\User $myMentors
*/
public function addUser(\Fitness\FitBundle\Entity\User $myMentors)
{
$this->my_mentors[] = $myMentors;
}
/**
* Get my_mentors
*
* #return Doctrine\Common\Collections\Collection
*/
public function getMyMentors()
{
return $this->my_mentors;
}
/**
* Get my_mentees
*
* #return Doctrine\Common\Collections\Collection
*/
public function getMyMentees()
{
return $this->my_mentees;
}
}
I created the self reference because a Mentee(which is a User) will subscribe to a Mentor(which is also a User). I tried to do this using the following function:
public function subscribeAction($menteeID, $mentorID)
{
$em = $this->getDoctrine()
->getEntityManager();
$mentor = $em->getRepository('TestBundle:User')
->find($mentorID);
$mentee = $em->getRepository('TestBundle:User')
->find($menteeID);
$currentMentors = $mentee->getMyMentors();
if ($currentMentors->contains($mentor))
$this->get('session')->setFlash('subscribe-notice', 'You have already signed up to this mentor!');
else
{
$mentee->setIsMentor(false);
$mentee->addUser($mentor);
$mentor->addUser($mentee);
$em->persist($mentee);
$em->persist($mentor);
$em->flush();
$this->get('session')->setFlash('subscribe-notice', 'Subscription succesful!');
}
return $this->redirect($this->generateUrl('TestBundle_testpage', array('id' => $mentor->getMentorProfile()->getId()) ));
}
The problem here is that when i check the database, it does not persist the data. The mentor-mentee relationship information is not stored in the table "mentor_and_mentee_relationship" as declared by the annotation.
I persisted both $mentor and $mentee in an attempt to get it to work, but apparently it doesnt.
Could my ORM annotation be declared wrongly?
You are using the same function (addUser) to add a mentor and to add a mentee. This is wrong. First you need two different setters in your entity (I changed the name of addUser to make it clear)
/**
* Add my_mentors
*
* #param Fitness\FitBundle\Entity\User $myMentors
*/
public function addMentor(\Fitness\FitBundle\Entity\User $myMentors)
{
$this->my_mentors[] = $myMentors;
}
/**
* Add my_mentees
*
* #param Fitness\FitBundle\Entity\User $myMentees
*/
public function addMentee(\Fitness\FitBundle\Entity\User $myMentees)
{
$this->my_mentees[] = $myMentees;
}
Then in your controller do:
$mentee->addMentor($mentor);
$mentor->addMentee($mentee);