Unable to add foreign key constraint after changing char set

4046 views mysql
6

I have a table that I'm using to store text messages in an app. I wanted to add the ability to use emojis but they kept showing up as ? ? ? ? etc... in the db. I learned that I needed to changes the character set of my table so I ran the command

ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin

but I got this error: Error Code: 1832. Cannot change column 'user_id': used in a foreign key constraint 'posts_ibfk_2'

so I dropped the FK and then ran the command and everything worked great. However now, when I try to readd the FK I keep getting a 1215 error that I can't add FK constraint.

ALTER TABLE posts
ADD FOREIGN KEY (user_id)
REFERENCES users(id);

I tried converting the char set back to what it was originally and am still unable to add the FK. How can I add it back?

answered question

You can't return a value from a void method (as you're finding out), and that's not how event-driven programming works. You either need a call-back mechanism of some sort, or use a modal-dialog such as a JOptionPane or modal JDialog. Please have a look at my answer in this similar question.

Also if PickSomething had a reference to the visualized Gui object, it could potentially "push" a value into this object, but this would result in tight coupling, and so isn't the best design.

And please read about Java naming conventions and avoid "_" underscore chars.

4 Answers

0

Notice the boldfaced points from the MySQL Docs:

Foreign key relationships involve a parent table that holds the central data values, and a child table with identical values pointing back to its parent. The FOREIGN KEY clause is specified in the child table. The parent and child tables must use the same storage engine.

Corresponding columns in the foreign key and the referenced key must have similar data types. The size and sign of integer types must be the same. The length of string types need not be the same. For nonbinary (character) string columns, the character set and collation must be the same.

When foreign_key_checks is enabled, which is the default setting, character set conversion is not permitted on tables that include a character string column used in a foreign key constraint.

posted this
11

I tried to write a method who returns it but the method does not appear in the list of all methods so it is impossible to call and I tried also to extends Pick_Something with Gui and make path protected but I had a StackOverflow error.

This is not working because it's 'saved' by JButton as an ActionListener (which is valid by the traits of inheritance).

Say you have some class Pick_Something that extends ActionListener as such:

public class Pick_Something implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        String path;
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION)
            path = selectElement.getSelectedFile().getAbsolutePath();
        else
            path = null;
    }
    pubic void otherMethod() {
        //doSomething
    }
}

If you want to call 'otherMethod()', then you'll either need to cast the object or save the object as type Pick_Something, as such:

public class Gui extends JFrame {
    private JButton myButton;
    private String path;
    //some other properties...

    public Gui () {
        myButton = new JButton("Some Text");
        Pick_Something ps = new Pick_Something();
        myButton.addActionListener(ps);
        ps.otherMethod();
    }
}

Or your other option:

public class Gui extends JFrame {
    private JButton myButton;
    private String path;
    some other properties...

    public Gui () {
        myButton = new JButton("Some Text");
        myButton.AddActionListener(new Pick_Something());
        Pick_Something ps = (Pick_Something) (myButton.getActionListeners()[0]);
        ps.otherMethod();
    }
}

Edit: Note that this is an example of being able to use the method externally. If you wanted to do so, you could call this method whenever the button is pressed by doing the following:

public class Pick_Something implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        otherMethod();
    }
    pubic void otherMethod() {
        //doSomething
    }
}

posted this
8

My question is: How could I return a variable which is in a class that implements an ActionListener into another class's variable?

You can't.

A quick look at the JavaDocs for ActionListener will show you that you the method does not return a value. I'm pretty sure that even if it could it would be worthless, as the only time your code knows the method has been triggered is when it's actually called.

The solution? Pass a "model" to the ActionListener implementation...

Start by defining a simple interface or contract...

public PathPicker {
    public void setPath(File path);
}

Then update PickSomething to accept instances of this interface...

public class PickSomething implements ActionListener {
    private PathPicker picker;

    public PickSomething(PathPicker picker) {
        this.picker = picker;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION) {
            picker.setPath(selectElement.getSelectedFile());
        } else {
            picker.setPath(null);
        }
    }
}

Now, all you need to do is implement the PathPicker interface, pass a reference of of it to PickSomething when you create and wait till it calls setPath.

This is commonly known as "delegation" (and ActionListener is also an example of it), where the actual responsibility to "delegated" to some other object. It's also an example of "observability", in a very simple sense, where the PickSomething can be observer by an instance of PathPicker for a change in its state (a path been selected).

It also decouples the code, as PathPicker doesn't care how the the path is set, only that it is notified when it is.

A note about paths...

File is an abstract representation of file system file or path. It has a lot of really cool functionality to make working with the filesystem easier and simpler.

Many APIs also take a reference of File to perform their operations. You should avoid converting a File to a String where ever possible, as you are going to rob yourself of this functionality and make your life more difficult in the long run

posted this
7

I would recommend using a call-back, something that Java-8's java.util.function supplies for you, and in fact a Consumer<String> would work perfectly here. Create your Consumer in the original class, and have the ActionListener class call its .accept(...) method, passing information directly from the listener class to the GUI with low coupling. For example if your Gui has a JTextField called filePathTxtField that you want filled with the user's file path of choice, one obtained by the ActionListener, then the consumer could look like so:

Consumer<String> consumer = (String text) -> {
    filePathTxtField.setText(text);
};

This would be created in the Gui class, and then passed into the ActionListener class via a constructor parameter:

// in the Gui class's constructor
button.addActionListener(new PickSomething(consumer));  

// the PickSomething class and its constructor
class PickSomething implements ActionListener {
    private Consumer<String> consumer;

    public PickSomething(Consumer<String> consumer) {
        this.consumer = consumer;
    }

Then the actionPerformed method could look like:

@Override
public void actionPerformed(ActionEvent e) {
    JFileChooser selectElement = new JFileChooser();
    String path;

    // get the path String and set it
    int status = selectElement.showOpenDialog(null);

    if (status == JFileChooser.APPROVE_OPTION) {
        path = selectElement.getSelectedFile().getAbsolutePath();
    } else {
        path = null;
    }

    // pass the path String into the Gui by calling the call-back method, passing it in
    consumer.accept(path);
}

The whole thing could look like:

import java.util.function.Consumer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class Gui extends JPanel {
    private JTextField filePathTxtField = new JTextField(45);
    private int foo = 0;

    public Gui() {
        filePathTxtField.setFocusable(false);
        add(filePathTxtField);

        JButton button = new JButton("Get File Path");
        Consumer<String> consumer = (String text) -> {
            filePathTxtField.setText(text);
        };
        button.addActionListener(new PickSomething(consumer));
        add(button);
    }

    private static void createAndShowGui() {
        Gui mainPanel = new Gui();

        JFrame frame = new JFrame("Gui");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.function.Consumer;
import javax.swing.JFileChooser;

public class PickSomething implements ActionListener {
    private Consumer<String> consumer;

    public PickSomething(Consumer<String> consumer) {
        this.consumer = consumer;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        String path;
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION) {
            path = selectElement.getSelectedFile().getAbsolutePath();
        } else {
            path = null;
        }
        consumer.accept(path);
    }
}   

posted this

Have an answer?

JD

Please login first before posting an answer.