A signature algorithm should - in general - be thought to contain the hashing method as parameter. Take for instance RSA / PSS. RSA / PSS includes the EMSA-PSS-ENCODE operation. Here the hash function to use is an option to the algorith.
Step 2 is:
Let mHash = Hash(M), an octet string of length hLen.
where M is the message to be signed.
"Or do I need to hash the plaintext to provide message integrity?" is therefore a question that is non-sensical for most signature definitions. RSA has deprecated modes that directly sign a message (with message recovery) but those are generally not used. ECDSA is also defined for use with a hash function and EC crypto cannot easily "encrypt the message" with the private key for signature generation.
As you have found out,
Signature dsa = Signature.getInstance("SHA256withECDSA"); uses SHA-256 as an option to ECDSA. The hashing will then be performed internally.
Sometimes hash value or intermediate hash value is used as input for signature generation. This can be useful when the device that performs the private key generation is limited in hashing bandwith. E.g. for a smart card you may not want to send all the data to the smart card - sending just the hash is much more efficient.